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

Files changed (28) hide show
  1. arelle/ModelRelationshipSet.py +3 -0
  2. arelle/ValidateDuplicateFacts.py +13 -7
  3. arelle/XbrlConst.py +3 -0
  4. arelle/_version.py +2 -2
  5. arelle/api/Session.py +88 -58
  6. arelle/plugin/validate/DBA/rules/fr.py +10 -10
  7. arelle/plugin/validate/DBA/rules/th.py +1 -1
  8. arelle/plugin/validate/ESEF/ESEF_Current/ValidateXbrlFinally.py +21 -20
  9. arelle/plugin/validate/NL/DisclosureSystems.py +14 -0
  10. arelle/plugin/validate/NL/PluginValidationDataExtension.py +167 -3
  11. arelle/plugin/validate/NL/ValidationPluginExtension.py +10 -1
  12. arelle/plugin/validate/NL/resources/config.xml +12 -1
  13. arelle/plugin/validate/NL/rules/fr_kvk.py +1 -1
  14. arelle/plugin/validate/NL/rules/nl_kvk.py +680 -155
  15. arelle/utils/validate/DetectScriptsInXhtml.py +1 -4
  16. arelle/utils/validate/ESEFImage.py +274 -0
  17. {arelle_release-2.37.21.dist-info → arelle_release-2.37.23.dist-info}/METADATA +1 -1
  18. {arelle_release-2.37.21.dist-info → arelle_release-2.37.23.dist-info}/RECORD +27 -26
  19. tests/integration_tests/validation/conformance_suite_configs.py +2 -0
  20. tests/integration_tests/validation/conformance_suite_configurations/nl_inline_2024.py +30 -43
  21. tests/integration_tests/validation/conformance_suite_configurations/nl_inline_2024_gaap_other.py +244 -0
  22. tests/integration_tests/validation/discover_tests.py +2 -2
  23. tests/unit_tests/arelle/plugin/validate/ESEF/ESEF_Current/test_validate_css_url.py +10 -2
  24. arelle/plugin/validate/ESEF/ESEF_Current/Image.py +0 -213
  25. {arelle_release-2.37.21.dist-info → arelle_release-2.37.23.dist-info}/WHEEL +0 -0
  26. {arelle_release-2.37.21.dist-info → arelle_release-2.37.23.dist-info}/entry_points.txt +0 -0
  27. {arelle_release-2.37.21.dist-info → arelle_release-2.37.23.dist-info}/licenses/LICENSE.md +0 -0
  28. {arelle_release-2.37.21.dist-info → arelle_release-2.37.23.dist-info}/top_level.txt +0 -0
@@ -3,29 +3,50 @@ See COPYRIGHT.md for copyright information.
3
3
  """
4
4
  from __future__ import annotations
5
5
 
6
+ from collections import defaultdict
7
+ from collections.abc import Iterable
6
8
  from datetime import date
9
+ from typing import Any, cast, TYPE_CHECKING
10
+
7
11
  import zipfile
8
12
 
13
+ from lxml.etree import Element
14
+
15
+ from arelle.ModelDtsObject import ModelLink, ModelResource, ModelType
9
16
  from arelle.ModelInstanceObject import ModelInlineFact
17
+ from arelle.ModelObject import ModelObject
18
+ from arelle.PrototypeDtsObject import PrototypeObject
10
19
  from arelle.ValidateDuplicateFacts import getDuplicateFactSets
11
20
  from arelle.XmlValidateConst import VALID
12
- from collections.abc import Iterable
13
- from typing import Any, cast, TYPE_CHECKING
14
21
 
15
- from arelle import XmlUtil, XbrlConst
22
+ from arelle import XbrlConst, XmlUtil
16
23
  from arelle.ValidateXbrl import ValidateXbrl
17
24
  from arelle.typing import TypeGetText
18
25
  from arelle.utils.PluginHooks import ValidationHook
19
26
  from arelle.utils.validate.Decorator import validation
27
+ from arelle.utils.validate.DetectScriptsInXhtml import containsScriptMarkers
28
+ from arelle.utils.validate.ESEFImage import ImageValidationParameters, validateImage
20
29
  from arelle.utils.validate.Validation import Validation
21
30
  from arelle.ValidateDuplicateFacts import getHashEquivalentFactGroups, getAspectEqualFacts
22
31
  from arelle.utils.validate.ValidationUtil import etreeIterWithDepth
23
- from ..DisclosureSystems import DISCLOSURE_SYSTEM_NL_INLINE_2024
32
+ from ..DisclosureSystems import (ALL_NL_INLINE_DISCLOSURE_SYSTEMS, NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
33
+ NL_INLINE_GAAP_OTHER_DISCLOSURE_SYSTEMS)
24
34
  from ..LinkbaseType import LinkbaseType
25
- from ..PluginValidationDataExtension import (PluginValidationDataExtension, ALLOWABLE_LANGUAGES,
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)
35
+ from ..PluginValidationDataExtension import (
36
+ PluginValidationDataExtension,
37
+ ALLOWABLE_LANGUAGES,
38
+ DEFAULT_MEMBER_ROLE_URI,
39
+ DISALLOWED_IXT_NAMESPACES,
40
+ EFFECTIVE_KVK_GAAP_IFRS_ENTRYPOINT_FILES,
41
+ EFFECTIVE_KVK_GAAP_OTHER_ENTRYPOINT_FILES,
42
+ MAX_REPORT_PACKAGE_SIZE_MBS,
43
+ NON_DIMENSIONALIZED_LINE_ITEM_LINKROLES,
44
+ QN_DOMAIN_ITEM_TYPES,
45
+ SUPPORTED_IMAGE_TYPES_BY_IS_FILE,
46
+ TAXONOMY_URLS_BY_YEAR,
47
+ XBRLI_IDENTIFIER_PATTERN,
48
+ XBRLI_IDENTIFIER_SCHEMA,
49
+ )
29
50
 
30
51
  if TYPE_CHECKING:
31
52
  from arelle.ModelXbrl import ModelXbrl
@@ -45,9 +66,7 @@ def _getReportingPeriodDateValue(modelXbrl: ModelXbrl, qname: QName) -> date | N
45
66
 
46
67
  @validation(
47
68
  hook=ValidationHook.XBRL_FINALLY,
48
- disclosureSystems=[
49
- DISCLOSURE_SYSTEM_NL_INLINE_2024
50
- ],
69
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
51
70
  )
52
71
  def rule_nl_kvk_3_1_1_1(
53
72
  pluginData: PluginValidationDataExtension,
@@ -64,7 +83,7 @@ def rule_nl_kvk_3_1_1_1(
64
83
  if not XBRLI_IDENTIFIER_PATTERN.match(entityId[1]):
65
84
  yield Validation.error(
66
85
  codes='NL.NL-KVK-RTS_Annex_IV_Par_2_G3-1-1_1.invalidIdentifierFormat',
67
- msg=_('xbrli:identifier content to match KVK number format that must consist of 8 consecutive digits.'
86
+ msg=_('xbrli:identifier content to match KVK number format that must consist of 8 consecutive digits. '
68
87
  'Additionally the first two digits must not be "00".'),
69
88
  modelObject = val.modelXbrl
70
89
  )
@@ -73,9 +92,7 @@ def rule_nl_kvk_3_1_1_1(
73
92
 
74
93
  @validation(
75
94
  hook=ValidationHook.XBRL_FINALLY,
76
- disclosureSystems=[
77
- DISCLOSURE_SYSTEM_NL_INLINE_2024
78
- ],
95
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
79
96
  )
80
97
  def rule_nl_kvk_3_1_1_2(
81
98
  pluginData: PluginValidationDataExtension,
@@ -91,7 +108,7 @@ def rule_nl_kvk_3_1_1_2(
91
108
  if XBRLI_IDENTIFIER_SCHEMA != entityId[0]:
92
109
  yield Validation.error(
93
110
  codes='NL.NL-KVK-RTS_Annex_IV_Par_2_G3-1-1_2.invalidIdentifier',
94
- msg=_('The scheme attribute of the xbrli:identifier does not match the required content.'
111
+ msg=_('The scheme attribute of the xbrli:identifier does not match the required content. '
95
112
  'This should be "http://www.kvk.nl/kvk-id".'),
96
113
  modelObject = val.modelXbrl
97
114
  )
@@ -100,9 +117,7 @@ def rule_nl_kvk_3_1_1_2(
100
117
 
101
118
  @validation(
102
119
  hook=ValidationHook.XBRL_FINALLY,
103
- disclosureSystems=[
104
- DISCLOSURE_SYSTEM_NL_INLINE_2024
105
- ],
120
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
106
121
  )
107
122
  def rule_nl_kvk_3_1_2_1(
108
123
  pluginData: PluginValidationDataExtension,
@@ -124,9 +139,7 @@ def rule_nl_kvk_3_1_2_1(
124
139
 
125
140
  @validation(
126
141
  hook=ValidationHook.XBRL_FINALLY,
127
- disclosureSystems=[
128
- DISCLOSURE_SYSTEM_NL_INLINE_2024
129
- ],
142
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
130
143
  )
131
144
  def rule_nl_kvk_3_1_2_2(
132
145
  pluginData: PluginValidationDataExtension,
@@ -148,9 +161,7 @@ def rule_nl_kvk_3_1_2_2(
148
161
 
149
162
  @validation(
150
163
  hook=ValidationHook.XBRL_FINALLY,
151
- disclosureSystems=[
152
- DISCLOSURE_SYSTEM_NL_INLINE_2024
153
- ],
164
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
154
165
  )
155
166
  def rule_nl_kvk_3_1_3_1 (
156
167
  pluginData: PluginValidationDataExtension,
@@ -172,9 +183,7 @@ def rule_nl_kvk_3_1_3_1 (
172
183
 
173
184
  @validation(
174
185
  hook=ValidationHook.XBRL_FINALLY,
175
- disclosureSystems=[
176
- DISCLOSURE_SYSTEM_NL_INLINE_2024
177
- ],
186
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
178
187
  )
179
188
  def rule_nl_kvk_3_1_3_2 (
180
189
  pluginData: PluginValidationDataExtension,
@@ -196,9 +205,7 @@ def rule_nl_kvk_3_1_3_2 (
196
205
 
197
206
  @validation(
198
207
  hook=ValidationHook.XBRL_FINALLY,
199
- disclosureSystems=[
200
- DISCLOSURE_SYSTEM_NL_INLINE_2024
201
- ],
208
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
202
209
  )
203
210
  def rule_nl_kvk_3_1_4_1 (
204
211
  pluginData: PluginValidationDataExtension,
@@ -221,9 +228,7 @@ def rule_nl_kvk_3_1_4_1 (
221
228
 
222
229
  @validation(
223
230
  hook=ValidationHook.XBRL_FINALLY,
224
- disclosureSystems=[
225
- DISCLOSURE_SYSTEM_NL_INLINE_2024
226
- ],
231
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
227
232
  )
228
233
  def rule_nl_kvk_3_1_4_2 (
229
234
  pluginData: PluginValidationDataExtension,
@@ -250,9 +255,7 @@ def rule_nl_kvk_3_1_4_2 (
250
255
 
251
256
  @validation(
252
257
  hook=ValidationHook.XBRL_FINALLY,
253
- disclosureSystems=[
254
- DISCLOSURE_SYSTEM_NL_INLINE_2024
255
- ],
258
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
256
259
  )
257
260
  def rule_nl_kvk_3_2_1_1 (
258
261
  pluginData: PluginValidationDataExtension,
@@ -277,9 +280,7 @@ def rule_nl_kvk_3_2_1_1 (
277
280
 
278
281
  @validation(
279
282
  hook=ValidationHook.XBRL_FINALLY,
280
- disclosureSystems=[
281
- DISCLOSURE_SYSTEM_NL_INLINE_2024
282
- ],
283
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
283
284
  )
284
285
  def rule_nl_kvk_3_2_3_1 (
285
286
  pluginData: PluginValidationDataExtension,
@@ -305,9 +306,7 @@ def rule_nl_kvk_3_2_3_1 (
305
306
 
306
307
  @validation(
307
308
  hook=ValidationHook.XBRL_FINALLY,
308
- disclosureSystems=[
309
- DISCLOSURE_SYSTEM_NL_INLINE_2024
310
- ],
309
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
311
310
  )
312
311
  def rule_nl_kvk_3_2_4_1 (
313
312
  pluginData: PluginValidationDataExtension,
@@ -335,9 +334,7 @@ def rule_nl_kvk_3_2_4_1 (
335
334
 
336
335
  @validation(
337
336
  hook=ValidationHook.XBRL_FINALLY,
338
- disclosureSystems=[
339
- DISCLOSURE_SYSTEM_NL_INLINE_2024
340
- ],
337
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
341
338
  )
342
339
  def rule_nl_kvk_3_2_4_2 (
343
340
  pluginData: PluginValidationDataExtension,
@@ -365,9 +362,7 @@ def rule_nl_kvk_3_2_4_2 (
365
362
 
366
363
  @validation(
367
364
  hook=ValidationHook.XBRL_FINALLY,
368
- disclosureSystems=[
369
- DISCLOSURE_SYSTEM_NL_INLINE_2024
370
- ],
365
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
371
366
  )
372
367
  def rule_nl_kvk_3_2_7_1 (
373
368
  pluginData: PluginValidationDataExtension,
@@ -386,7 +381,7 @@ def rule_nl_kvk_3_2_7_1 (
386
381
  if len(improperlyEscapedFacts) >0:
387
382
  yield Validation.error(
388
383
  codes='NL.NL-KVK.3.2.7.1.improperApplicationOfEscapeAttribute',
389
- msg=_('Ensure that any block-tagged facts of type textBlockItemType are assigned @escape="true",'
384
+ msg=_('Ensure that any block-tagged facts of type textBlockItemType are assigned @escape="true", '
390
385
  'while other data types (e.g., xbrli:stringItemType) are assigned @escape="false".'),
391
386
  modelObject = improperlyEscapedFacts
392
387
  )
@@ -394,9 +389,29 @@ def rule_nl_kvk_3_2_7_1 (
394
389
 
395
390
  @validation(
396
391
  hook=ValidationHook.XBRL_FINALLY,
397
- disclosureSystems=[
398
- DISCLOSURE_SYSTEM_NL_INLINE_2024
399
- ],
392
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
393
+ )
394
+ def rule_nl_kvk_3_2_8_1(
395
+ pluginData: PluginValidationDataExtension,
396
+ val: ValidateXbrl,
397
+ *args: Any,
398
+ **kwargs: Any,
399
+ ) -> Iterable[Validation]:
400
+ """
401
+ NL-KVK.3.2.8.1: Include unique @id attribute for each tagged fact
402
+ """
403
+ errors = {fact for fact in val.modelXbrl.facts if not fact.id}
404
+ if len(errors) > 0:
405
+ yield Validation.warning(
406
+ codes='NL.NL-KVK.3.2.8.1',
407
+ msg=_('All facts should include an id attribute'),
408
+ modelObject=errors
409
+ )
410
+
411
+
412
+ @validation(
413
+ hook=ValidationHook.XBRL_FINALLY,
414
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
400
415
  )
401
416
  def rule_nl_kvk_3_3_1_1 (
402
417
  pluginData: PluginValidationDataExtension,
@@ -417,9 +432,7 @@ def rule_nl_kvk_3_3_1_1 (
417
432
 
418
433
  @validation(
419
434
  hook=ValidationHook.XBRL_FINALLY,
420
- disclosureSystems=[
421
- DISCLOSURE_SYSTEM_NL_INLINE_2024
422
- ],
435
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
423
436
  )
424
437
  def rule_nl_kvk_3_3_1_2 (
425
438
  pluginData: PluginValidationDataExtension,
@@ -441,9 +454,7 @@ def rule_nl_kvk_3_3_1_2 (
441
454
 
442
455
  @validation(
443
456
  hook=ValidationHook.XBRL_FINALLY,
444
- disclosureSystems=[
445
- DISCLOSURE_SYSTEM_NL_INLINE_2024
446
- ],
457
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
447
458
  )
448
459
  def rule_nl_kvk_3_3_1_3 (
449
460
  pluginData: PluginValidationDataExtension,
@@ -467,9 +478,7 @@ def rule_nl_kvk_3_3_1_3 (
467
478
 
468
479
  @validation(
469
480
  hook=ValidationHook.XBRL_FINALLY,
470
- disclosureSystems=[
471
- DISCLOSURE_SYSTEM_NL_INLINE_2024
472
- ],
481
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
473
482
  )
474
483
  def rule_nl_kvk_3_4_1_1 (
475
484
  pluginData: PluginValidationDataExtension,
@@ -491,9 +500,7 @@ def rule_nl_kvk_3_4_1_1 (
491
500
 
492
501
  @validation(
493
502
  hook=ValidationHook.XBRL_FINALLY,
494
- disclosureSystems=[
495
- DISCLOSURE_SYSTEM_NL_INLINE_2024
496
- ],
503
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
497
504
  )
498
505
  def rule_nl_kvk_3_4_1_2 (
499
506
  pluginData: PluginValidationDataExtension,
@@ -515,9 +522,7 @@ def rule_nl_kvk_3_4_1_2 (
515
522
 
516
523
  @validation(
517
524
  hook=ValidationHook.XBRL_FINALLY,
518
- disclosureSystems=[
519
- DISCLOSURE_SYSTEM_NL_INLINE_2024
520
- ],
525
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
521
526
  )
522
527
  def rule_nl_kvk_3_4_1_3 (
523
528
  pluginData: PluginValidationDataExtension,
@@ -541,9 +546,7 @@ def rule_nl_kvk_3_4_1_3 (
541
546
 
542
547
  @validation(
543
548
  hook=ValidationHook.XBRL_FINALLY,
544
- disclosureSystems=[
545
- DISCLOSURE_SYSTEM_NL_INLINE_2024
546
- ],
549
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
547
550
  )
548
551
  def rule_nl_kvk_3_4_1_4 (
549
552
  pluginData: PluginValidationDataExtension,
@@ -565,9 +568,7 @@ def rule_nl_kvk_3_4_1_4 (
565
568
 
566
569
  @validation(
567
570
  hook=ValidationHook.XBRL_FINALLY,
568
- disclosureSystems=[
569
- DISCLOSURE_SYSTEM_NL_INLINE_2024
570
- ],
571
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
571
572
  )
572
573
  def rule_nl_kvk_3_4_1_5 (
573
574
  pluginData: PluginValidationDataExtension,
@@ -589,9 +590,7 @@ def rule_nl_kvk_3_4_1_5 (
589
590
 
590
591
  @validation(
591
592
  hook=ValidationHook.XBRL_FINALLY,
592
- disclosureSystems=[
593
- DISCLOSURE_SYSTEM_NL_INLINE_2024
594
- ],
593
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
595
594
  )
596
595
  def rule_nl_kvk_3_4_2_1 (
597
596
  pluginData: PluginValidationDataExtension,
@@ -613,9 +612,73 @@ def rule_nl_kvk_3_4_2_1 (
613
612
 
614
613
  @validation(
615
614
  hook=ValidationHook.XBRL_FINALLY,
616
- disclosureSystems=[
617
- DISCLOSURE_SYSTEM_NL_INLINE_2024
618
- ],
615
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
616
+ )
617
+ def rule_nl_kvk_3_5_1_1_non_img (
618
+ pluginData: PluginValidationDataExtension,
619
+ val: ValidateXbrl,
620
+ *args: Any,
621
+ **kwargs: Any,
622
+ ) -> Iterable[Validation]:
623
+ """
624
+ NL-KVK.3.5.1.1: Resources embedded or referenced by the XHTML document and its inline XBRL MUST NOT contain executable code.
625
+ """
626
+
627
+ executableElements = []
628
+ for ixdsHtmlRootElt in val.modelXbrl.ixdsHtmlElements:
629
+ for elt in ixdsHtmlRootElt.iter(Element):
630
+ if containsScriptMarkers(elt):
631
+ executableElements.append(elt)
632
+ if executableElements:
633
+ yield Validation.error(
634
+ codes='NL.NL-KVK.3.5.1.1.executableCodePresent',
635
+ msg=_("Resources embedded or referenced by the XHTML document and its inline XBRL MUST NOT contain executable code."),
636
+ modelObject=executableElements,
637
+ )
638
+
639
+
640
+ @validation(
641
+ hook=ValidationHook.XBRL_FINALLY,
642
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
643
+ )
644
+ def rule_nl_kvk_3_5_1_img (
645
+ pluginData: PluginValidationDataExtension,
646
+ val: ValidateXbrl,
647
+ *args: Any,
648
+ **kwargs: Any,
649
+ ) -> Iterable[Validation]:
650
+ """
651
+ NL-KVK.3.5.1.1: Inline XBRL images MUST NOT contain executable code.
652
+ NL-KVK.3.5.1.2: Images included in the XHTML document MUST be saved with MIME type specifying PNG, GIF, SVG or JPG/JPEG formats.
653
+ NL-KVK.3.5.1.3: File type inferred from file signature does not match the data URL media subtype (MIME subtype).
654
+ NL-KVK.3.5.1.4: File type inferred from file signature does not match the file extension.
655
+ NL-KVK.3.5.1.5: Images included in the XHTML document MUST be saved in PNG, GIF, SVG or JPG/JPEG formats.
656
+ """
657
+
658
+ imageValidationParameters = ImageValidationParameters.from_non_esef(
659
+ checkMinExternalResourceSize=False,
660
+ missingMimeTypeIsIncorrect=False,
661
+ recommendBase64EncodingEmbeddedImages=False,
662
+ supportedImgTypes=SUPPORTED_IMAGE_TYPES_BY_IS_FILE,
663
+ )
664
+ for ixdsHtmlRootElt in val.modelXbrl.ixdsHtmlElements:
665
+ for elt in ixdsHtmlRootElt.iter((f'{{{XbrlConst.xhtml}}}img', '{http://www.w3.org/2000/svg}svg')):
666
+ src = elt.get('src', '').strip()
667
+ evaluatedMsg = _('On line {line}, "alt" attribute value: "{alt}"').format(line=elt.sourceline, alt=elt.get('alt'))
668
+ yield from validateImage(
669
+ elt.modelDocument.baseForElement(elt),
670
+ src,
671
+ val.modelXbrl,
672
+ val,
673
+ elt,
674
+ evaluatedMsg,
675
+ imageValidationParameters,
676
+ )
677
+
678
+
679
+ @validation(
680
+ hook=ValidationHook.XBRL_FINALLY,
681
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
619
682
  )
620
683
  def rule_nl_kvk_3_5_2_1(
621
684
  pluginData: PluginValidationDataExtension,
@@ -624,7 +687,7 @@ def rule_nl_kvk_3_5_2_1(
624
687
  **kwargs: Any,
625
688
  ) -> Iterable[Validation]:
626
689
  """
627
- NL-KVK.3.5.2.1: Each tagged text fact MUST have the xml:lang attribute assigned or inherited.
690
+ NL-KVK.3.5.2.1: Each tagged text fact MUST have the 'xml:lang' attribute assigned or inherited.
628
691
  """
629
692
  factsWithoutLang = []
630
693
  for fact in val.modelXbrl.facts:
@@ -637,16 +700,14 @@ def rule_nl_kvk_3_5_2_1(
637
700
  if len(factsWithoutLang) > 0:
638
701
  yield Validation.error(
639
702
  codes='NL.NL-KVK.3.5.2.1.undefinedLanguageForTextFact',
640
- msg=_('Each tagged text fact MUST have the xml:lang attribute assigned or inherited.'),
703
+ msg=_("Each tagged text fact MUST have the 'xml:lang' attribute assigned or inherited."),
641
704
  modelObject=factsWithoutLang
642
705
  )
643
706
 
644
707
 
645
708
  @validation(
646
709
  hook=ValidationHook.XBRL_FINALLY,
647
- disclosureSystems=[
648
- DISCLOSURE_SYSTEM_NL_INLINE_2024
649
- ],
710
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
650
711
  )
651
712
  def rule_nl_kvk_3_5_2_2(
652
713
  pluginData: PluginValidationDataExtension,
@@ -675,9 +736,7 @@ def rule_nl_kvk_3_5_2_2(
675
736
 
676
737
  @validation(
677
738
  hook=ValidationHook.XBRL_FINALLY,
678
- disclosureSystems=[
679
- DISCLOSURE_SYSTEM_NL_INLINE_2024
680
- ],
739
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
681
740
  )
682
741
  def rule_nl_kvk_3_5_2_3(
683
742
  pluginData: PluginValidationDataExtension,
@@ -706,9 +765,7 @@ def rule_nl_kvk_3_5_2_3(
706
765
 
707
766
  @validation(
708
767
  hook=ValidationHook.XBRL_FINALLY,
709
- disclosureSystems=[
710
- DISCLOSURE_SYSTEM_NL_INLINE_2024
711
- ],
768
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
712
769
  )
713
770
  def rule_nl_kvk_3_5_3_1(
714
771
  pluginData: PluginValidationDataExtension,
@@ -730,9 +787,7 @@ def rule_nl_kvk_3_5_3_1(
730
787
 
731
788
  @validation(
732
789
  hook=ValidationHook.XBRL_FINALLY,
733
- disclosureSystems=[
734
- DISCLOSURE_SYSTEM_NL_INLINE_2024
735
- ],
790
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
736
791
  )
737
792
  def rule_nl_kvk_3_5_4_1 (
738
793
  pluginData: PluginValidationDataExtension,
@@ -755,9 +810,7 @@ def rule_nl_kvk_3_5_4_1 (
755
810
 
756
811
  @validation(
757
812
  hook=ValidationHook.XBRL_FINALLY,
758
- disclosureSystems=[
759
- DISCLOSURE_SYSTEM_NL_INLINE_2024
760
- ],
813
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
761
814
  )
762
815
  def rule_nl_kvk_3_6_3_1(
763
816
  pluginData: PluginValidationDataExtension,
@@ -788,9 +841,7 @@ def rule_nl_kvk_3_6_3_1(
788
841
 
789
842
  @validation(
790
843
  hook=ValidationHook.XBRL_FINALLY,
791
- disclosureSystems=[
792
- DISCLOSURE_SYSTEM_NL_INLINE_2024
793
- ],
844
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
794
845
  )
795
846
  def rule_nl_kvk_3_6_3_2(
796
847
  pluginData: PluginValidationDataExtension,
@@ -820,9 +871,7 @@ def rule_nl_kvk_3_6_3_2(
820
871
 
821
872
  @validation(
822
873
  hook=ValidationHook.XBRL_FINALLY,
823
- disclosureSystems=[
824
- DISCLOSURE_SYSTEM_NL_INLINE_2024
825
- ],
874
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
826
875
  )
827
876
  def rule_nl_kvk_3_6_3_3(
828
877
  pluginData: PluginValidationDataExtension,
@@ -850,9 +899,7 @@ def rule_nl_kvk_3_6_3_3(
850
899
 
851
900
  @validation(
852
901
  hook=ValidationHook.FINALLY,
853
- disclosureSystems=[
854
- DISCLOSURE_SYSTEM_NL_INLINE_2024
855
- ],
902
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
856
903
  )
857
904
  def rule_nl_kvk_3_7_1_1(
858
905
  pluginData: PluginValidationDataExtension,
@@ -880,9 +927,7 @@ def rule_nl_kvk_3_7_1_1(
880
927
 
881
928
  @validation(
882
929
  hook=ValidationHook.FINALLY,
883
- disclosureSystems=[
884
- DISCLOSURE_SYSTEM_NL_INLINE_2024
885
- ],
930
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
886
931
  )
887
932
  def rule_nl_kvk_3_7_1_2(
888
933
  pluginData: PluginValidationDataExtension,
@@ -910,9 +955,7 @@ def rule_nl_kvk_3_7_1_2(
910
955
 
911
956
  @validation(
912
957
  hook=ValidationHook.XBRL_FINALLY,
913
- disclosureSystems=[
914
- DISCLOSURE_SYSTEM_NL_INLINE_2024
915
- ],
958
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
916
959
  )
917
960
  def rule_nl_kvk_4_1_1_1(
918
961
  pluginData: PluginValidationDataExtension,
@@ -963,9 +1006,7 @@ def rule_nl_kvk_4_1_1_1(
963
1006
 
964
1007
  @validation(
965
1008
  hook=ValidationHook.XBRL_FINALLY,
966
- disclosureSystems=[
967
- DISCLOSURE_SYSTEM_NL_INLINE_2024
968
- ],
1009
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
969
1010
  )
970
1011
  def rule_nl_kvk_4_1_1_2(
971
1012
  pluginData: PluginValidationDataExtension,
@@ -1000,9 +1041,7 @@ def rule_nl_kvk_4_1_1_2(
1000
1041
 
1001
1042
  @validation(
1002
1043
  hook=ValidationHook.XBRL_FINALLY,
1003
- disclosureSystems=[
1004
- DISCLOSURE_SYSTEM_NL_INLINE_2024
1005
- ],
1044
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
1006
1045
  )
1007
1046
  def rule_nl_kvk_4_1_2_1(
1008
1047
  pluginData: PluginValidationDataExtension,
@@ -1027,9 +1066,7 @@ def rule_nl_kvk_4_1_2_1(
1027
1066
 
1028
1067
  @validation(
1029
1068
  hook=ValidationHook.XBRL_FINALLY,
1030
- disclosureSystems=[
1031
- DISCLOSURE_SYSTEM_NL_INLINE_2024
1032
- ],
1069
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
1033
1070
  )
1034
1071
  def rule_nl_kvk_4_1_2_2(
1035
1072
  pluginData: PluginValidationDataExtension,
@@ -1038,7 +1075,7 @@ def rule_nl_kvk_4_1_2_2(
1038
1075
  **kwargs: Any,
1039
1076
  ) -> Iterable[Validation]:
1040
1077
  """
1041
- NL-KVK.4.1.2.2: The legal entitys extension taxonomy MUST import the applicable version of
1078
+ NL-KVK.4.1.2.2: The legal entity's extension taxonomy MUST import the applicable version of
1042
1079
  the taxonomy files prepared by KVK.
1043
1080
  """
1044
1081
  reportingPeriod = pluginData.getReportingPeriod(val.modelXbrl)
@@ -1048,16 +1085,15 @@ def rule_nl_kvk_4_1_2_2(
1048
1085
  yield Validation.error(
1049
1086
  codes='NL.NL-KVK.4.1.2.2.incorrectKvkTaxonomyVersionUsed',
1050
1087
  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.'),
1088
+ 'for the reported financial reporting period. Verify the taxonomy version and make sure '
1089
+ 'that FinancialReportingPeriod are tagged correctly.'),
1052
1090
  modelObject=val.modelXbrl.modelDocument
1053
1091
  )
1054
1092
 
1055
1093
 
1056
1094
  @validation(
1057
1095
  hook=ValidationHook.XBRL_FINALLY,
1058
- disclosureSystems=[
1059
- DISCLOSURE_SYSTEM_NL_INLINE_2024
1060
- ],
1096
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
1061
1097
  )
1062
1098
  def rule_nl_kvk_4_1_5_1(
1063
1099
  pluginData: PluginValidationDataExtension,
@@ -1089,9 +1125,7 @@ def rule_nl_kvk_4_1_5_1(
1089
1125
 
1090
1126
  @validation(
1091
1127
  hook=ValidationHook.XBRL_FINALLY,
1092
- disclosureSystems=[
1093
- DISCLOSURE_SYSTEM_NL_INLINE_2024
1094
- ],
1128
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
1095
1129
  )
1096
1130
  def rule_nl_kvk_4_1_5_2(
1097
1131
  pluginData: PluginValidationDataExtension,
@@ -1122,9 +1156,7 @@ def rule_nl_kvk_4_1_5_2(
1122
1156
 
1123
1157
  @validation(
1124
1158
  hook=ValidationHook.XBRL_FINALLY,
1125
- disclosureSystems=[
1126
- DISCLOSURE_SYSTEM_NL_INLINE_2024
1127
- ],
1159
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
1128
1160
  )
1129
1161
  def rule_nl_kvk_4_2_0_1(
1130
1162
  pluginData: PluginValidationDataExtension,
@@ -1148,9 +1180,7 @@ def rule_nl_kvk_4_2_0_1(
1148
1180
 
1149
1181
  @validation(
1150
1182
  hook=ValidationHook.XBRL_FINALLY,
1151
- disclosureSystems=[
1152
- DISCLOSURE_SYSTEM_NL_INLINE_2024
1153
- ],
1183
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
1154
1184
  )
1155
1185
  def rule_nl_kvk_4_2_0_2(
1156
1186
  pluginData: PluginValidationDataExtension,
@@ -1174,9 +1204,7 @@ def rule_nl_kvk_4_2_0_2(
1174
1204
 
1175
1205
  @validation(
1176
1206
  hook=ValidationHook.XBRL_FINALLY,
1177
- disclosureSystems=[
1178
- DISCLOSURE_SYSTEM_NL_INLINE_2024
1179
- ],
1207
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
1180
1208
  )
1181
1209
  def rule_nl_kvk_4_2_1_1(
1182
1210
  pluginData: PluginValidationDataExtension,
@@ -1205,9 +1233,127 @@ def rule_nl_kvk_4_2_1_1(
1205
1233
 
1206
1234
  @validation(
1207
1235
  hook=ValidationHook.XBRL_FINALLY,
1208
- disclosureSystems=[
1209
- DISCLOSURE_SYSTEM_NL_INLINE_2024
1210
- ],
1236
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
1237
+ )
1238
+ def rule_nl_kvk_4_2_2_2(
1239
+ pluginData: PluginValidationDataExtension,
1240
+ val: ValidateXbrl,
1241
+ *args: Any,
1242
+ **kwargs: Any,
1243
+ ) -> Iterable[Validation]:
1244
+ """
1245
+ NL-KVK.4.2.2.2: Domain members MUST have domainItemType data type as defined in https://www.xbrl.org/dtr/type/2022-03-31/types.xsd.
1246
+ """
1247
+ domainMembersWrongType = []
1248
+ domainMembers = pluginData.getDimensionalData(val.modelXbrl).domainMembers
1249
+ extensionData = pluginData.getExtensionData(val.modelXbrl)
1250
+ for concept in extensionData.extensionConcepts:
1251
+ if concept.isDomainMember and concept in domainMembers and concept.typeQname not in QN_DOMAIN_ITEM_TYPES:
1252
+ domainMembersWrongType.append(concept)
1253
+ if len(domainMembersWrongType) > 0:
1254
+ yield Validation.error(
1255
+ codes='NL.NL-KVK.4.2.2.2.domainMemberWrongDataType',
1256
+ modelObject=domainMembersWrongType,
1257
+ msg=_('Domain members must have domainItemType data type as defined in "https://www.xbrl.org/dtr/type/2022-03-31/types.xsd".'
1258
+ 'Update to follow appropriate Data Type Registry. '))
1259
+
1260
+
1261
+ @validation(
1262
+ hook=ValidationHook.XBRL_FINALLY,
1263
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
1264
+ )
1265
+ def rule_nl_kvk_4_2_3_1(
1266
+ pluginData: PluginValidationDataExtension,
1267
+ val: ValidateXbrl,
1268
+ *args: Any,
1269
+ **kwargs: Any,
1270
+ ) -> Iterable[Validation]:
1271
+ """
1272
+ NL-KVK.4.2.3.1: Extension taxonomy MUST NOT define typed dimensions.
1273
+ """
1274
+ typedDims = []
1275
+ extensionData = pluginData.getExtensionData(val.modelXbrl)
1276
+ for concept in extensionData.extensionConcepts:
1277
+ if concept.isTypedDimension:
1278
+ typedDims.append(concept)
1279
+ if len(typedDims) > 0:
1280
+ yield Validation.error(
1281
+ codes='NL.NL-KVK.4.2.3.1.typedDimensionDefinitionInExtensionTaxonomy',
1282
+ modelObject=typedDims,
1283
+ msg=_('Typed dimensions are not allowed in the extension taxonomy. Update to remove the typed dimension.'))
1284
+
1285
+
1286
+ @validation(
1287
+ hook=ValidationHook.XBRL_FINALLY,
1288
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
1289
+ )
1290
+ def rule_nl_kvk_4_3_1_1(
1291
+ pluginData: PluginValidationDataExtension,
1292
+ val: ValidateXbrl,
1293
+ *args: Any,
1294
+ **kwargs: Any,
1295
+ ) -> Iterable[Validation]:
1296
+ """
1297
+ NL-KVK.4.3.1.1: Anchoring relationships for elements other than concepts MUST not
1298
+ use 'http://www.esma.europa.eu/xbrl/esef/arcrole/wider-narrower' arcrole
1299
+ """
1300
+ anchorData = pluginData.getAnchorData(val.modelXbrl)
1301
+ if len(anchorData.extLineItemsWronglyAnchored) > 0:
1302
+ yield Validation.error(
1303
+ codes='NL.NL-KVK.4.3.1.1.unexpectedAnchoringRelationshipsDefinedUsingWiderNarrowerArcrole',
1304
+ modelObject=anchorData.extLineItemsWronglyAnchored,
1305
+ msg=_('A custom element that is not a line item concept is using the wider-narrower arcrole. '
1306
+ 'Only line item concepts should use this arcrole. '
1307
+ 'Update the extension to no longer include this arcole.')
1308
+ )
1309
+ for anchor in anchorData.anchorsWithDomainItem:
1310
+ yield Validation.error(
1311
+ codes="NL.NL-KVK.4.3.1.1.unexpectedAnchoringRelationshipsDefinedUsingWiderNarrowerArcrole",
1312
+ msg=_("Anchoring relationships MUST be from and to concepts, from %(qname1)s to %(qname2)s"),
1313
+ modelObject=(anchor, anchor.fromModelObject, anchor.toModelObject),
1314
+ qname1=anchor.fromModelObject.qname,
1315
+ qname2=anchor.toModelObject.qname
1316
+ )
1317
+ for anchor in anchorData.anchorsWithDimensionItem:
1318
+ yield Validation.error(
1319
+ codes="NL.NL-KVK.4.3.1.1.unexpectedAnchoringRelationshipsDefinedUsingWiderNarrowerArcrole",
1320
+ msg=_("Anchoring relationships MUST be from and to concepts, from %(qname1)s to %(qname2)s"),
1321
+ modelObject=(anchor, anchor.fromModelObject, anchor.toModelObject),
1322
+ qname1=anchor.fromModelObject.qname,
1323
+ qname2=anchor.toModelObject.qname
1324
+ )
1325
+
1326
+
1327
+ @validation(
1328
+ hook=ValidationHook.XBRL_FINALLY,
1329
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
1330
+ )
1331
+ def rule_nl_kvk_4_3_2_1(
1332
+ pluginData: PluginValidationDataExtension,
1333
+ val: ValidateXbrl,
1334
+ *args: Any,
1335
+ **kwargs: Any,
1336
+ ) -> Iterable[Validation]:
1337
+ """
1338
+ NL-KVK.4.3.2.1: Anchoring relationships for concepts MUST be defined in a dedicated
1339
+ extended link role (or roles if needed to properly represent the relationships),
1340
+ e.g. http://{default pattern for roles}/Anchoring.
1341
+ """
1342
+ anchorData = pluginData.getAnchorData(val.modelXbrl)
1343
+ for elr, rels in anchorData.anchorsInDimensionalElrs.items():
1344
+ yield Validation.error(
1345
+ codes="NL.NL-KVK.4.3.2.1.anchoringRelationshipsForConceptsDefinedInElrContainingDimensionalRelationships",
1346
+ msg=_("Anchoring relationships for concepts MUST be defined in a dedicated extended link role "
1347
+ "(or roles if needed to properly represent the relationships), e.g. "
1348
+ "http://{issuer default pattern for roles}/Anchoring. %(anchoringDimensionalELR)s"),
1349
+ modelObject=rels,
1350
+ anchoringDimensionalELR=elr
1351
+ )
1352
+
1353
+
1354
+ @validation(
1355
+ hook=ValidationHook.XBRL_FINALLY,
1356
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
1211
1357
  )
1212
1358
  def rule_nl_kvk_4_4_1_1(
1213
1359
  pluginData: PluginValidationDataExtension,
@@ -1235,9 +1381,7 @@ def rule_nl_kvk_4_4_1_1(
1235
1381
 
1236
1382
  @validation(
1237
1383
  hook=ValidationHook.XBRL_FINALLY,
1238
- disclosureSystems=[
1239
- DISCLOSURE_SYSTEM_NL_INLINE_2024
1240
- ],
1384
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
1241
1385
  )
1242
1386
  def rule_nl_kvk_4_4_2_1(
1243
1387
  pluginData: PluginValidationDataExtension,
@@ -1264,9 +1408,7 @@ def rule_nl_kvk_4_4_2_1(
1264
1408
 
1265
1409
  @validation(
1266
1410
  hook=ValidationHook.XBRL_FINALLY,
1267
- disclosureSystems=[
1268
- DISCLOSURE_SYSTEM_NL_INLINE_2024
1269
- ],
1411
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
1270
1412
  )
1271
1413
  def rule_nl_kvk_4_4_2_2(
1272
1414
  pluginData: PluginValidationDataExtension,
@@ -1276,7 +1418,7 @@ def rule_nl_kvk_4_4_2_2(
1276
1418
  ) -> Iterable[Validation]:
1277
1419
  """
1278
1420
  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”.
1421
+ http://xbrl.org/int/dim/arcrole/all arcrole MUST have xbrldt:closed attribute set to "true".
1280
1422
  """
1281
1423
  errors = []
1282
1424
  extensionData = pluginData.getExtensionData(val.modelXbrl)
@@ -1294,9 +1436,7 @@ def rule_nl_kvk_4_4_2_2(
1294
1436
 
1295
1437
  @validation(
1296
1438
  hook=ValidationHook.XBRL_FINALLY,
1297
- disclosureSystems=[
1298
- DISCLOSURE_SYSTEM_NL_INLINE_2024
1299
- ],
1439
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
1300
1440
  )
1301
1441
  def rule_nl_kvk_4_4_2_3(
1302
1442
  pluginData: PluginValidationDataExtension,
@@ -1306,7 +1446,7 @@ def rule_nl_kvk_4_4_2_3(
1306
1446
  ) -> Iterable[Validation]:
1307
1447
  """
1308
1448
  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”.
1449
+ http://xbrl.org/int/dim/arcrole/notAll arcrole MUST have xbrldt:closed attribute set to "false".
1310
1450
  """
1311
1451
  errors = []
1312
1452
  extensionData = pluginData.getExtensionData(val.modelXbrl)
@@ -1324,9 +1464,308 @@ def rule_nl_kvk_4_4_2_3(
1324
1464
 
1325
1465
  @validation(
1326
1466
  hook=ValidationHook.XBRL_FINALLY,
1327
- disclosureSystems=[
1328
- DISCLOSURE_SYSTEM_NL_INLINE_2024
1329
- ],
1467
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
1468
+ )
1469
+ def rule_nl_kvk_4_4_2_4(
1470
+ pluginData: PluginValidationDataExtension,
1471
+ val: ValidateXbrl,
1472
+ *args: Any,
1473
+ **kwargs: Any,
1474
+ ) -> Iterable[Validation]:
1475
+ """
1476
+ NL-KVK.4.4.2.4: Line items that do not require any dimensional information to tag data MUST be linked to the hypercube in the dedicated
1477
+ extended link role
1478
+ """
1479
+ elrPrimaryItems = pluginData.getDimensionalData(val.modelXbrl).elrPrimaryItems
1480
+ errors = set(concept
1481
+ for qn, facts in val.modelXbrl.factsByQname.items()
1482
+ if any(not f.context.qnameDims for f in facts if f.context is not None)
1483
+ for concept in (val.modelXbrl.qnameConcepts.get(qn),)
1484
+ if concept is not None and
1485
+ not any(concept in elrPrimaryItems.get(lr, set()) for lr in NON_DIMENSIONALIZED_LINE_ITEM_LINKROLES) and
1486
+ concept not in elrPrimaryItems.get("*", set()))
1487
+ for error in errors:
1488
+ yield Validation.error(
1489
+ codes='NL.NL-KVK.4.4.2.4.extensionTaxonomyLineItemNotLinkedToAnyHypercube',
1490
+ modelObject=error,
1491
+ msg=_('A non-dimensional concept was not associated to a hypercube. Update relationship so concept is linked to a hypercube.'),
1492
+ )
1493
+
1494
+ @validation(
1495
+ hook=ValidationHook.XBRL_FINALLY,
1496
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
1497
+ )
1498
+ def rule_nl_kvk_4_4_3_1(
1499
+ pluginData: PluginValidationDataExtension,
1500
+ val: ValidateXbrl,
1501
+ *args: Any,
1502
+ **kwargs: Any,
1503
+ ) -> Iterable[Validation]:
1504
+ """
1505
+ NL-KVK.4.4.3.1: The extension taxonomy MUST not modify (prohibit and/or override) default members assigned to dimensions by the KVK taxonomy
1506
+ """
1507
+ for modelLink in cast(list[ModelLink], val.modelXbrl.baseSets[XbrlConst.dimensionDefault, None, None, None]):
1508
+ if not pluginData.isExtensionUri(modelLink.modelDocument.uri, val.modelXbrl):
1509
+ continue
1510
+ for linkChild in modelLink:
1511
+ if (
1512
+ isinstance(linkChild,(ModelObject,PrototypeObject))
1513
+ and linkChild.get(XbrlConst.qnXlinkType.clarkNotation) == "arc"
1514
+ and linkChild.get(XbrlConst.qnXlinkArcRole.clarkNotation) == XbrlConst.dimensionDefault
1515
+ ):
1516
+ fromLabel = linkChild.get(XbrlConst.qnXlinkFrom.clarkNotation)
1517
+ for fromResource in modelLink.labeledResources[fromLabel]:
1518
+ if not pluginData.isExtensionUri(fromResource.modelDocument.uri, val.modelXbrl):
1519
+ yield Validation.error(
1520
+ codes='NL.NL-KVK.4.4.3.1.extensionTaxonomyOverridesDefaultMembers',
1521
+ msg=_('A default member does not match the default member settings of the taxonomy. '
1522
+ 'Update the default member to taxonomy defaults.'
1523
+ ),
1524
+ modelObject=linkChild
1525
+ )
1526
+ extensionData = pluginData.getExtensionData(val.modelXbrl)
1527
+ for modelDocument, extensionDocumentData in extensionData.extensionDocuments.items():
1528
+ for arc in extensionDocumentData.iterArcsByType(LinkbaseType.DEFINITION, includeArcroles={XbrlConst.dimensionDefault}):
1529
+ if arc.get("use") == "prohibited":
1530
+ yield Validation.error(
1531
+ codes='NL.NL-KVK.4.4.3.1.extensionTaxonomyOverridesDefaultMembers',
1532
+ msg=_('A default member is forbidden in the extension taxonomy. '
1533
+ 'Update the default member to taxonomy defaults.'
1534
+ ),
1535
+ modelObject=arc
1536
+ )
1537
+
1538
+
1539
+ @validation(
1540
+ hook=ValidationHook.XBRL_FINALLY,
1541
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
1542
+ )
1543
+ def rule_nl_kvk_4_4_3_2(
1544
+ pluginData: PluginValidationDataExtension,
1545
+ val: ValidateXbrl,
1546
+ *args: Any,
1547
+ **kwargs: Any,
1548
+ ) -> Iterable[Validation]:
1549
+ """
1550
+ NL-KVK.4.4.3.2: Each dimension in an extension taxonomy MUST be assigned to a default member in the ELR with role URI https://www.nltaxonomie.nl/kvk/role/axis-defaults.
1551
+ """
1552
+ dimensionDefaults =val.modelXbrl.relationshipSet(XbrlConst.dimensionDefault, DEFAULT_MEMBER_ROLE_URI)
1553
+ extensionData = pluginData.getExtensionData(val.modelXbrl)
1554
+ for modelConcept in extensionData.extensionConcepts:
1555
+ if modelConcept.isExplicitDimension and not dimensionDefaults.fromModelObject(modelConcept):
1556
+ yield Validation.error(
1557
+ codes='NL.NL-KVK.4.4.2.3.extensionTaxonomyDimensionNotAssignedDefaultMemberInDedicatedPlaceholder',
1558
+ modelObject=modelConcept,
1559
+ msg=_('Axis is missing a default member or the default member does not match the taxonomy defaults. '
1560
+ 'Update to set default member based on taxonomy defaults.'
1561
+ ),
1562
+ )
1563
+
1564
+
1565
+ @validation(
1566
+ hook=ValidationHook.XBRL_FINALLY,
1567
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
1568
+ )
1569
+ def rule_nl_kvk_4_4_4_1(
1570
+ pluginData: PluginValidationDataExtension,
1571
+ val: ValidateXbrl,
1572
+ *args: Any,
1573
+ **kwargs: Any,
1574
+ ) -> Iterable[Validation]:
1575
+ """
1576
+ NL-KVK.4.4.5.1: Custom labels roles SHOULD NOT be used.
1577
+ """
1578
+ warnings = set()
1579
+ for ELR in val.modelXbrl.relationshipSet(XbrlConst.parentChild).linkRoleUris:
1580
+ relSet = val.modelXbrl.relationshipSet(XbrlConst.parentChild, ELR)
1581
+ for rootConcept in relSet.rootConcepts:
1582
+ warnings = pluginData.checkLabels(set(), val.modelXbrl , rootConcept, relSet, None, set())
1583
+ if len(warnings) > 0:
1584
+ yield Validation.warning(
1585
+ codes='NL.NL-KVK.4.4.4.1.missingPreferredLabelRole',
1586
+ modelObject=warnings,
1587
+ msg=_('Multiple concepts exist in the presentation with the same label role. '
1588
+ 'Review presentation if duplicate concepts should exist or separate preferred label roles should be set.'),
1589
+ )
1590
+
1591
+
1592
+ @validation(
1593
+ hook=ValidationHook.XBRL_FINALLY,
1594
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
1595
+ )
1596
+ def rule_nl_kvk_4_4_5_1(
1597
+ pluginData: PluginValidationDataExtension,
1598
+ val: ValidateXbrl,
1599
+ *args: Any,
1600
+ **kwargs: Any,
1601
+ ) -> Iterable[Validation]:
1602
+ """
1603
+ NL-KVK.4.4.5.1: Custom labels roles SHOULD NOT be used.
1604
+ """
1605
+ warnings = []
1606
+ labelsRelationshipSet = val.modelXbrl.relationshipSet(XbrlConst.conceptLabel)
1607
+ if not labelsRelationshipSet:
1608
+ return
1609
+ for labelRels in labelsRelationshipSet.fromModelObjects().values():
1610
+ for labelRel in labelRels:
1611
+ label = cast(ModelResource, labelRel.toModelObject)
1612
+ if label.role in XbrlConst.standardLabelRoles:
1613
+ continue
1614
+ roleType = val.modelXbrl.roleTypes.get(label.role)
1615
+ if roleType is not None and \
1616
+ roleType[0].modelDocument.uri.startswith("http://www.xbrl.org/lrr"):
1617
+ continue
1618
+ warnings.append(label)
1619
+ if len(warnings) > 0:
1620
+ yield Validation.warning(
1621
+ codes='NL.NL-KVK.4.4.5.1.taxonomyElementLabelCustomRole',
1622
+ modelObject=warnings,
1623
+ msg=_('A custom label role has been used. Update to label role to non-custom.'),
1624
+ )
1625
+
1626
+
1627
+ @validation(
1628
+ hook=ValidationHook.XBRL_FINALLY,
1629
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
1630
+ )
1631
+ def rule_nl_kvk_4_4_5_2(
1632
+ pluginData: PluginValidationDataExtension,
1633
+ val: ValidateXbrl,
1634
+ *args: Any,
1635
+ **kwargs: Any,
1636
+ ) -> Iterable[Validation]:
1637
+ """
1638
+ NL-KVK.4.4.5.2: Extension taxonomy elements SHOULD be assigned with at most one label for any combination of role and language.
1639
+ Additionally, extension taxonomies shall not override or replace standard labels of elements referenced in the KVK taxonomy.
1640
+ """
1641
+ labelsRelationshipSet = val.modelXbrl.relationshipSet(XbrlConst.conceptLabel)
1642
+ extensionData = pluginData.getExtensionData(val.modelXbrl)
1643
+ extensionConcepts = extensionData.extensionConcepts
1644
+ for concept in val.modelXbrl.qnameConcepts.values():
1645
+ conceptLangRoleLabels = defaultdict(list)
1646
+ labelRels = labelsRelationshipSet.fromModelObject(concept)
1647
+ for labelRel in labelRels:
1648
+ label = cast(ModelResource, labelRel.toModelObject)
1649
+ conceptLangRoleLabels[(label.xmlLang, label.role)].append(labelRel.toModelObject)
1650
+ for (lang, labelRole), labels in conceptLangRoleLabels.items():
1651
+ if concept in extensionConcepts and len(labels) > 1:
1652
+ yield Validation.error(
1653
+ codes='NL.NL-KVK.4.4.5.2.taxonomyElementDuplicateLabels',
1654
+ msg=_('A concept was found with more than one label role for related language. '
1655
+ 'Update to only one combination. Language: %(lang)s, Role: %(labelRole)s, Concept: %(concept)s.'),
1656
+ modelObject=[concept]+labels, concept=concept.qname, lang=lang, labelRole=labelRole,
1657
+ )
1658
+ elif labelRole == XbrlConst.standardLabel:
1659
+ hasCoreLabel = False
1660
+ hasExtensionLabel = False
1661
+ for label in labels:
1662
+ if pluginData.isExtensionUri(label.modelDocument.uri, val.modelXbrl):
1663
+ hasExtensionLabel = True
1664
+ else:
1665
+ hasCoreLabel = True
1666
+ if hasCoreLabel and hasExtensionLabel:
1667
+ labels_files = ['"%s": %s' % (l.text, l.modelDocument.basename) for l in labels]
1668
+ yield Validation.error(
1669
+ codes='NL.NL-KVK.4.4.5.2.taxonomyElementDuplicateLabels',
1670
+ msg=_("An extension taxonomy defines a standard label for a concept "
1671
+ "already labeled by the base taxonomy. Language: %(lang)s, "
1672
+ "Role: %(labelRole)s, Concept: %(concept)s, Labels: %(labels)s"),
1673
+ modelObject=[concept]+labels, concept=concept.qname, lang=lang,
1674
+ labelRole=labelRole, labels=", ".join(labels_files),
1675
+ )
1676
+
1677
+
1678
+ @validation(
1679
+ hook=ValidationHook.XBRL_FINALLY,
1680
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
1681
+ )
1682
+ def rule_nl_kvk_4_4_6_1(
1683
+ pluginData: PluginValidationDataExtension,
1684
+ val: ValidateXbrl,
1685
+ *args: Any,
1686
+ **kwargs: Any,
1687
+ ) -> Iterable[Validation]:
1688
+ """
1689
+ NL-KVK.4.4.6.1: All usable concepts in extension taxonomy relationships SHOULD be applied by tagged facts.
1690
+ """
1691
+ conceptsUsed = {f.concept for f in val.modelXbrl.facts}
1692
+ unreportedLbLocs = set()
1693
+ for arcrole in (XbrlConst.parentChild, XbrlConst.summationItems, XbrlConst.all, XbrlConst.dimensionDomain, XbrlConst.domainMember):
1694
+ for rel in val.modelXbrl.relationshipSet(arcrole).modelRelationships:
1695
+ for object in (rel.fromModelObject, rel.toModelObject):
1696
+ if (object is None or
1697
+ object.isAbstract or
1698
+ object in conceptsUsed or
1699
+ not pluginData.isExtensionUri(rel.modelDocument.uri, val.modelXbrl)):
1700
+ continue
1701
+ if arcrole in (XbrlConst.parentChild, XbrlConst.summationItems):
1702
+ unreportedLbLocs.add(rel.fromLocator)
1703
+ elif object.type is not None and rel.isUsable and not object.type.isDomainItemType:
1704
+ unreportedLbLocs.add(rel.fromLocator)
1705
+ if len(unreportedLbLocs) > 0:
1706
+ yield Validation.warning(
1707
+ # Subtitle is capitalized inconsistently here because is tmatches the conformance suite. This may change in the future.
1708
+ codes='NL.NL-KVK.4.4.6.1.UsableConceptsNotAppliedByTaggedFacts',
1709
+ modelObject=unreportedLbLocs,
1710
+ msg=_('Axis is missing a default member or the default member does not match the taxonomy defaults. '
1711
+ 'Update to set default member based on taxonomy defaults.')
1712
+ )
1713
+
1714
+
1715
+ @validation(
1716
+ hook=ValidationHook.XBRL_FINALLY,
1717
+ disclosureSystems=NL_INLINE_GAAP_OTHER_DISCLOSURE_SYSTEMS,
1718
+ )
1719
+ def rule_nl_kvk_5_1_3_1(
1720
+ pluginData: PluginValidationDataExtension,
1721
+ val: ValidateXbrl,
1722
+ *args: Any,
1723
+ **kwargs: Any,
1724
+ ) -> Iterable[Validation]:
1725
+ """
1726
+ NL-KVK.5.1.3.1: Validate that the imported taxonomy matches the KVK-specified entry point.
1727
+ - https://www.nltaxonomie.nl/kvk/2024-12-31/kvk-annual-report-other-gaap.xsd
1728
+ """
1729
+ uris = {doc[0].uri for doc in val.modelXbrl.namespaceDocs.values()}
1730
+ matches = uris & EFFECTIVE_KVK_GAAP_OTHER_ENTRYPOINT_FILES
1731
+ if not matches:
1732
+ yield Validation.error(
1733
+ codes='NL.NL-KVK.5.1.3.1.requiredEntryPointOtherGaapNotReferenced',
1734
+ msg=_('The extension taxonomy must import the entry point of the taxonomy files prepared by KVK.'),
1735
+ modelObject=val.modelXbrl.modelDocument
1736
+ )
1737
+
1738
+
1739
+ @validation(
1740
+ hook=ValidationHook.XBRL_FINALLY,
1741
+ disclosureSystems=NL_INLINE_GAAP_OTHER_DISCLOSURE_SYSTEMS,
1742
+ )
1743
+ def rule_nl_kvk_5_1_3_2(
1744
+ pluginData: PluginValidationDataExtension,
1745
+ val: ValidateXbrl,
1746
+ *args: Any,
1747
+ **kwargs: Any,
1748
+ ) -> Iterable[Validation]:
1749
+ """
1750
+ NL-KVK.5.1.3.2: The legal entity's report MUST import the applicable version of
1751
+ the taxonomy files prepared by KVK.
1752
+ """
1753
+ reportingPeriod = pluginData.getReportingPeriod(val.modelXbrl)
1754
+ uris = {doc[0].uri for doc in val.modelXbrl.namespaceDocs.values()}
1755
+ matches = uris & TAXONOMY_URLS_BY_YEAR.get(reportingPeriod or '', set())
1756
+ if not reportingPeriod or not matches:
1757
+ yield Validation.error(
1758
+ codes='NL.NL-KVK.5.1.3.2.incorrectVersionEntryPointOtherGaapReferenced',
1759
+ msg=_('The report MUST import the applicable version of the taxonomy files prepared by KVK '
1760
+ 'for the reported financial reporting period. Verify the taxonomy version and make sure '
1761
+ 'that FinancialReportingPeriod are tagged correctly.'),
1762
+ modelObject=val.modelXbrl.modelDocument
1763
+ )
1764
+
1765
+
1766
+ @validation(
1767
+ hook=ValidationHook.XBRL_FINALLY,
1768
+ disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
1330
1769
  )
1331
1770
  def rule_nl_kvk_6_1_1_1(
1332
1771
  pluginData: PluginValidationDataExtension,
@@ -1348,3 +1787,89 @@ def rule_nl_kvk_6_1_1_1(
1348
1787
  msg=_('The size of the report package must not exceed %(maxSize)s MBs, size is %(size)s MBs.'),
1349
1788
  modelObject=val.modelXbrl, maxSize=MAX_REPORT_PACKAGE_SIZE_MBS, size=int(_size/1000000)
1350
1789
  )
1790
+
1791
+
1792
+ @validation(
1793
+ hook=ValidationHook.XBRL_FINALLY,
1794
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
1795
+ )
1796
+ def rule_nl_kvk_RTS_Annex_IV_Par_11_G4_2_2_1(
1797
+ pluginData: PluginValidationDataExtension,
1798
+ val: ValidateXbrl,
1799
+ *args: Any,
1800
+ **kwargs: Any,
1801
+ ) -> Iterable[Validation]:
1802
+ """
1803
+ NL-KVK.RTS_Annex_IV_Par_11_G4-2-2_1: Extension taxonomy MUST NOT define a custom type if a matching
1804
+ type is defined by the XBRL 2.1 specification or in the XBRL Data Types Registry.
1805
+ Similar to ESEF.RTS.Annex.IV.Par.11.customDataTypeDuplicatingXbrlOrDtrEntry
1806
+ """
1807
+ errors = []
1808
+ extensionData = pluginData.getExtensionData(val.modelXbrl)
1809
+ for modelDocument, extensionDocumentData in extensionData.extensionDocuments.items():
1810
+ for modelType in modelDocument.xmlRootElement.iterdescendants(tag=XbrlConst.qnXsdComplexType.clarkNotation):
1811
+ if isinstance(modelType, ModelType) and \
1812
+ modelType.typeDerivedFrom is not None and \
1813
+ modelType.typeDerivedFrom.qname.namespaceURI == XbrlConst.xbrli and \
1814
+ not modelType.particlesList:
1815
+ errors.append(modelType)
1816
+ if len(errors) > 0:
1817
+ yield Validation.error(
1818
+ codes='NL.NL-KVK.RTS_Annex_IV_Par_11_G4-2-2_1.customTypeAlreadyDefinedByXbrl',
1819
+ msg=_('A custom data type is being used that matches a standard data type from the XBRL Data Type Registry. '
1820
+ 'Update to remove duplicate data types and leverage the standard where appropriate.'),
1821
+ modelObject=errors
1822
+ )
1823
+
1824
+
1825
+ @validation(
1826
+ hook=ValidationHook.XBRL_FINALLY,
1827
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
1828
+ )
1829
+ def rule_nl_kvk_RTS_Annex_IV_Par_4_2(
1830
+ pluginData: PluginValidationDataExtension,
1831
+ val: ValidateXbrl,
1832
+ *args: Any,
1833
+ **kwargs: Any,
1834
+ ) -> Iterable[Validation]:
1835
+ """
1836
+ NL-KVK.RTS_Annex_IV_Par_4_2: Extension elements must be equipped with an appropriate balance attribute.
1837
+ """
1838
+ errors = []
1839
+ for concept in pluginData.getExtensionConcepts(val.modelXbrl):
1840
+ if concept.isMonetary and concept.balance is None:
1841
+ errors.append(concept)
1842
+ if len(errors) > 0:
1843
+ yield Validation.error(
1844
+ codes='NL.NL-KVK.RTS_Annex_IV_Par_4_2.monetaryConceptWithoutBalance',
1845
+ msg=_('Extension elements must have an appropriate balance attribute.'),
1846
+ modelObject=errors
1847
+ )
1848
+
1849
+
1850
+ @validation(
1851
+ hook=ValidationHook.XBRL_FINALLY,
1852
+ disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
1853
+ )
1854
+ def rule_nl_kvk_RTS_Annex_IV_Par_6(
1855
+ pluginData: PluginValidationDataExtension,
1856
+ val: ValidateXbrl,
1857
+ *args: Any,
1858
+ **kwargs: Any,
1859
+ ) -> Iterable[Validation]:
1860
+ """
1861
+ NL-KVK.RTS_Annex_IV_Par_6: Each NL-GAAP or IFRS financial statements structure MUST be equipped with
1862
+ a calculation linkbase
1863
+ """
1864
+ hasCalcLinkbase = False
1865
+ extensionData = pluginData.getExtensionData(val.modelXbrl)
1866
+ for modelDoc, extensionDoc in extensionData.extensionDocuments.items():
1867
+ for linkbase in extensionDoc.linkbases:
1868
+ if linkbase.linkbaseType == LinkbaseType.CALCULATION:
1869
+ hasCalcLinkbase = True
1870
+ if not hasCalcLinkbase:
1871
+ yield Validation.error(
1872
+ codes='NL.NL-KVK.RTS_Annex_IV_Par_6.extensionTaxonomyWrongFilesStructure',
1873
+ msg=_('The filing package must include a calculation linkbase.'),
1874
+ modelObject=val.modelXbrl.modelDocument
1875
+ )