arelle-release 2.37.71__py3-none-any.whl → 2.38.0__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.
Files changed (71) hide show
  1. arelle/BetaFeatures.py +0 -21
  2. arelle/Cntlr.py +7 -1
  3. arelle/CntlrCmdLine.py +95 -19
  4. arelle/CntlrWinMain.py +4 -1
  5. arelle/DialogFind.py +1 -1
  6. arelle/ModelDtsObject.py +2 -0
  7. arelle/ModelObject.py +16 -18
  8. arelle/ModelObjectFactory.py +17 -15
  9. arelle/ModelXbrl.py +7 -1
  10. arelle/PluginManager.py +1 -5
  11. arelle/RuntimeOptions.py +1 -0
  12. arelle/UrlUtil.py +11 -0
  13. arelle/Validate.py +3 -3
  14. arelle/ValidateXbrl.py +2 -1
  15. arelle/ValidateXbrlCalcs.py +210 -186
  16. arelle/WebCache.py +2 -8
  17. arelle/XbrlConst.py +2 -0
  18. arelle/XmlUtil.py +16 -21
  19. arelle/XmlValidate.py +4 -6
  20. arelle/_version.py +2 -2
  21. arelle/config/rosettaEntitlements.plist +8 -0
  22. arelle/conformance/CSVTestcaseLoader.py +1 -1
  23. arelle/formula/XPathContext.py +3 -3
  24. arelle/logging/formatters/LogFormatter.py +3 -1
  25. arelle/packages/report/ReportPackage.py +9 -1
  26. arelle/plugin/inlineXbrlDocumentSet.py +1 -3
  27. arelle/plugin/validate/DBA/DisclosureSystems.py +19 -1
  28. arelle/plugin/validate/DBA/resources/config.xml +5 -0
  29. arelle/plugin/validate/DBA/rules/fr.py +19 -2
  30. arelle/plugin/validate/DBA/rules/tc.py +2 -0
  31. arelle/plugin/validate/DBA/rules/th.py +6 -0
  32. arelle/plugin/validate/DBA/rules/tm.py +18 -5
  33. arelle/plugin/validate/DBA/rules/tr.py +11 -5
  34. arelle/plugin/validate/EDINET/ControllerPluginData.py +2 -1
  35. arelle/plugin/validate/EDINET/NamespaceConfig.py +50 -0
  36. arelle/plugin/validate/EDINET/PluginValidationDataExtension.py +33 -78
  37. arelle/plugin/validate/EDINET/TableOfContentsBuilder.py +153 -51
  38. arelle/plugin/validate/EDINET/rules/contexts.py +1 -1
  39. arelle/plugin/validate/EDINET/rules/edinet.py +163 -20
  40. arelle/plugin/validate/EDINET/rules/gfm.py +88 -1
  41. arelle/plugin/validate/EDINET/rules/upload.py +1 -1
  42. arelle/plugin/validate/ESEF/ESEF_2021/ValidateXbrlFinally.py +3 -3
  43. arelle/plugin/validate/ESEF/ESEF_Current/DTS.py +42 -14
  44. arelle/plugin/validate/ESEF/ESEF_Current/ValidateXbrlFinally.py +14 -3
  45. arelle/plugin/validate/ESEF/__init__.py +10 -5
  46. arelle/plugin/validate/ESEF/resources/authority-validations.json +10 -5
  47. arelle/plugin/validate/NL/DisclosureSystems.py +22 -0
  48. arelle/plugin/validate/NL/PluginValidationDataExtension.py +20 -0
  49. arelle/plugin/validate/NL/ValidationPluginExtension.py +48 -3
  50. arelle/plugin/validate/NL/resources/config.xml +18 -0
  51. arelle/plugin/validate/NL/rules/br_kvk.py +9 -54
  52. arelle/plugin/validate/NL/rules/fg_nl.py +7 -38
  53. arelle/plugin/validate/NL/rules/fr_kvk.py +7 -42
  54. arelle/plugin/validate/NL/rules/fr_nl.py +25 -140
  55. arelle/plugin/validate/NL/rules/nl_kvk.py +125 -12
  56. arelle/plugin/validate/ROS/rules/ros.py +3 -1
  57. arelle/plugin/validate/UK/__init__.py +70 -14
  58. arelle/utils/EntryPointDetection.py +17 -11
  59. arelle/utils/validate/ESEFImage.py +3 -3
  60. arelle/utils/validate/Validation.py +9 -0
  61. arelle/utils/validate/ValidationPlugin.py +14 -12
  62. {arelle_release-2.37.71.dist-info → arelle_release-2.38.0.dist-info}/METADATA +10 -5
  63. {arelle_release-2.37.71.dist-info → arelle_release-2.38.0.dist-info}/RECORD +67 -69
  64. {arelle_release-2.37.71.dist-info → arelle_release-2.38.0.dist-info}/licenses/LICENSE.md +0 -3
  65. arelle/model/CommentBase.py +0 -9
  66. arelle/model/ElementBase.py +0 -11
  67. arelle/model/PIBase.py +0 -10
  68. arelle/model/__init__.py +0 -15
  69. {arelle_release-2.37.71.dist-info → arelle_release-2.38.0.dist-info}/WHEEL +0 -0
  70. {arelle_release-2.37.71.dist-info → arelle_release-2.38.0.dist-info}/entry_points.txt +0 -0
  71. {arelle_release-2.37.71.dist-info → arelle_release-2.38.0.dist-info}/top_level.txt +0 -0
@@ -22,12 +22,7 @@ from arelle.utils.Contexts import partitionModelXbrlContexts
22
22
  from arelle.utils.PluginHooks import ValidationHook
23
23
  from arelle.utils.validate.Decorator import validation
24
24
  from arelle.utils.validate.Validation import Validation
25
- from ..DisclosureSystems import (
26
- DISCLOSURE_SYSTEM_NT16,
27
- DISCLOSURE_SYSTEM_NT17,
28
- DISCLOSURE_SYSTEM_NT18,
29
- DISCLOSURE_SYSTEM_NT19,
30
- )
25
+ from ..DisclosureSystems import NT_DISCLOSURE_SYSTEMS
31
26
  from ..PluginValidationDataExtension import PluginValidationDataExtension
32
27
 
33
28
  _: TypeGetText
@@ -72,12 +67,7 @@ XHTML_LIST_ITEM_TYPES = {
72
67
 
73
68
  @validation(
74
69
  hook=ValidationHook.XBRL_FINALLY,
75
- disclosureSystems=[
76
- DISCLOSURE_SYSTEM_NT16,
77
- DISCLOSURE_SYSTEM_NT17,
78
- DISCLOSURE_SYSTEM_NT18,
79
- DISCLOSURE_SYSTEM_NT19,
80
- ],
70
+ disclosureSystems=NT_DISCLOSURE_SYSTEMS,
81
71
  )
82
72
  def rule_fr_nl_1_02(
83
73
  pluginData: PluginValidationDataExtension,
@@ -123,12 +113,7 @@ def rule_fr_nl_1_02(
123
113
 
124
114
  @validation(
125
115
  hook=ValidationHook.XBRL_FINALLY,
126
- disclosureSystems=[
127
- DISCLOSURE_SYSTEM_NT16,
128
- DISCLOSURE_SYSTEM_NT17,
129
- DISCLOSURE_SYSTEM_NT18,
130
- DISCLOSURE_SYSTEM_NT19,
131
- ],
116
+ disclosureSystems=NT_DISCLOSURE_SYSTEMS,
132
117
  )
133
118
  def rule_fr_nl_1_03(
134
119
  pluginData: PluginValidationDataExtension,
@@ -151,12 +136,7 @@ def rule_fr_nl_1_03(
151
136
 
152
137
  @validation(
153
138
  hook=ValidationHook.XBRL_FINALLY,
154
- disclosureSystems=[
155
- DISCLOSURE_SYSTEM_NT16,
156
- DISCLOSURE_SYSTEM_NT17,
157
- DISCLOSURE_SYSTEM_NT18,
158
- DISCLOSURE_SYSTEM_NT19,
159
- ],
139
+ disclosureSystems=NT_DISCLOSURE_SYSTEMS,
160
140
  )
161
141
  def rule_fr_nl_1_04(
162
142
  pluginData: PluginValidationDataExtension,
@@ -211,12 +191,7 @@ def rule_fr_nl_1_04(
211
191
 
212
192
  @validation(
213
193
  hook=ValidationHook.XBRL_FINALLY,
214
- disclosureSystems=[
215
- DISCLOSURE_SYSTEM_NT16,
216
- DISCLOSURE_SYSTEM_NT17,
217
- DISCLOSURE_SYSTEM_NT18,
218
- DISCLOSURE_SYSTEM_NT19,
219
- ],
194
+ disclosureSystems=NT_DISCLOSURE_SYSTEMS,
220
195
  )
221
196
  def rule_fr_nl_1_05(
222
197
  pluginData: PluginValidationDataExtension,
@@ -239,12 +214,7 @@ def rule_fr_nl_1_05(
239
214
 
240
215
  @validation(
241
216
  hook=ValidationHook.XBRL_FINALLY,
242
- disclosureSystems=[
243
- DISCLOSURE_SYSTEM_NT16,
244
- DISCLOSURE_SYSTEM_NT17,
245
- DISCLOSURE_SYSTEM_NT18,
246
- DISCLOSURE_SYSTEM_NT19,
247
- ],
217
+ disclosureSystems=NT_DISCLOSURE_SYSTEMS,
248
218
  )
249
219
  def rule_fr_nl_1_06(
250
220
  pluginData: PluginValidationDataExtension,
@@ -273,12 +243,7 @@ def rule_fr_nl_1_06(
273
243
 
274
244
  @validation(
275
245
  hook=ValidationHook.XBRL_FINALLY,
276
- disclosureSystems=[
277
- DISCLOSURE_SYSTEM_NT16,
278
- DISCLOSURE_SYSTEM_NT17,
279
- DISCLOSURE_SYSTEM_NT18,
280
- DISCLOSURE_SYSTEM_NT19,
281
- ],
246
+ disclosureSystems=NT_DISCLOSURE_SYSTEMS,
282
247
  )
283
248
  def rule_fr_nl_1_01(
284
249
  pluginData: PluginValidationDataExtension,
@@ -307,12 +272,7 @@ def rule_fr_nl_1_01(
307
272
 
308
273
  @validation(
309
274
  hook=ValidationHook.XBRL_FINALLY,
310
- disclosureSystems=[
311
- DISCLOSURE_SYSTEM_NT16,
312
- DISCLOSURE_SYSTEM_NT17,
313
- DISCLOSURE_SYSTEM_NT18,
314
- DISCLOSURE_SYSTEM_NT19,
315
- ],
275
+ disclosureSystems=NT_DISCLOSURE_SYSTEMS,
316
276
  )
317
277
  def rule_fr_nl_2_03(
318
278
  pluginData: PluginValidationDataExtension,
@@ -337,12 +297,7 @@ def rule_fr_nl_2_03(
337
297
 
338
298
  @validation(
339
299
  hook=ValidationHook.XBRL_FINALLY,
340
- disclosureSystems=[
341
- DISCLOSURE_SYSTEM_NT16,
342
- DISCLOSURE_SYSTEM_NT17,
343
- DISCLOSURE_SYSTEM_NT18,
344
- DISCLOSURE_SYSTEM_NT19,
345
- ],
300
+ disclosureSystems=NT_DISCLOSURE_SYSTEMS,
346
301
  )
347
302
  def rule_fr_nl_2_04(
348
303
  pluginData: PluginValidationDataExtension,
@@ -369,12 +324,7 @@ def rule_fr_nl_2_04(
369
324
 
370
325
  @validation(
371
326
  hook=ValidationHook.XBRL_FINALLY,
372
- disclosureSystems=[
373
- DISCLOSURE_SYSTEM_NT16,
374
- DISCLOSURE_SYSTEM_NT17,
375
- DISCLOSURE_SYSTEM_NT18,
376
- DISCLOSURE_SYSTEM_NT19,
377
- ],
327
+ disclosureSystems=NT_DISCLOSURE_SYSTEMS,
378
328
  )
379
329
  def rule_fr_nl_2_05(
380
330
  pluginData: PluginValidationDataExtension,
@@ -401,12 +351,7 @@ def rule_fr_nl_2_05(
401
351
 
402
352
  @validation(
403
353
  hook=ValidationHook.XBRL_FINALLY,
404
- disclosureSystems=[
405
- DISCLOSURE_SYSTEM_NT16,
406
- DISCLOSURE_SYSTEM_NT17,
407
- DISCLOSURE_SYSTEM_NT18,
408
- DISCLOSURE_SYSTEM_NT19,
409
- ],
354
+ disclosureSystems=NT_DISCLOSURE_SYSTEMS,
410
355
  )
411
356
  def rule_fr_nl_2_06(
412
357
  pluginData: PluginValidationDataExtension,
@@ -449,12 +394,7 @@ def rule_fr_nl_2_06(
449
394
 
450
395
  @validation(
451
396
  hook=ValidationHook.XBRL_FINALLY,
452
- disclosureSystems=[
453
- DISCLOSURE_SYSTEM_NT16,
454
- DISCLOSURE_SYSTEM_NT17,
455
- DISCLOSURE_SYSTEM_NT18,
456
- DISCLOSURE_SYSTEM_NT19,
457
- ],
397
+ disclosureSystems=NT_DISCLOSURE_SYSTEMS,
458
398
  )
459
399
  def rule_fr_nl_2_07(
460
400
  pluginData: PluginValidationDataExtension,
@@ -476,12 +416,7 @@ def rule_fr_nl_2_07(
476
416
 
477
417
  @validation(
478
418
  hook=ValidationHook.XBRL_FINALLY,
479
- disclosureSystems=[
480
- DISCLOSURE_SYSTEM_NT16,
481
- DISCLOSURE_SYSTEM_NT17,
482
- DISCLOSURE_SYSTEM_NT18,
483
- DISCLOSURE_SYSTEM_NT19,
484
- ],
419
+ disclosureSystems=NT_DISCLOSURE_SYSTEMS,
485
420
  )
486
421
  def rule_fr_nl_3_01(
487
422
  pluginData: PluginValidationDataExtension,
@@ -509,12 +444,7 @@ def rule_fr_nl_3_01(
509
444
 
510
445
  @validation(
511
446
  hook=ValidationHook.XBRL_FINALLY,
512
- disclosureSystems=[
513
- DISCLOSURE_SYSTEM_NT16,
514
- DISCLOSURE_SYSTEM_NT17,
515
- DISCLOSURE_SYSTEM_NT18,
516
- DISCLOSURE_SYSTEM_NT19,
517
- ],
447
+ disclosureSystems=NT_DISCLOSURE_SYSTEMS,
518
448
  )
519
449
  def rule_fr_nl_3_02(
520
450
  pluginData: PluginValidationDataExtension,
@@ -536,12 +466,7 @@ def rule_fr_nl_3_02(
536
466
 
537
467
  @validation(
538
468
  hook=ValidationHook.XBRL_FINALLY,
539
- disclosureSystems=[
540
- DISCLOSURE_SYSTEM_NT16,
541
- DISCLOSURE_SYSTEM_NT17,
542
- DISCLOSURE_SYSTEM_NT18,
543
- DISCLOSURE_SYSTEM_NT19,
544
- ],
469
+ disclosureSystems=NT_DISCLOSURE_SYSTEMS,
545
470
  )
546
471
  def rule_fr_nl_3_03(
547
472
  pluginData: PluginValidationDataExtension,
@@ -564,12 +489,7 @@ def rule_fr_nl_3_03(
564
489
 
565
490
  @validation(
566
491
  hook=ValidationHook.XBRL_FINALLY,
567
- disclosureSystems=[
568
- DISCLOSURE_SYSTEM_NT16,
569
- DISCLOSURE_SYSTEM_NT17,
570
- DISCLOSURE_SYSTEM_NT18,
571
- DISCLOSURE_SYSTEM_NT19,
572
- ],
492
+ disclosureSystems=NT_DISCLOSURE_SYSTEMS,
573
493
  )
574
494
  def rule_fr_nl_3_04(
575
495
  pluginData: PluginValidationDataExtension,
@@ -591,12 +511,7 @@ def rule_fr_nl_3_04(
591
511
 
592
512
  @validation(
593
513
  hook=ValidationHook.XBRL_FINALLY,
594
- disclosureSystems=[
595
- DISCLOSURE_SYSTEM_NT16,
596
- DISCLOSURE_SYSTEM_NT17,
597
- DISCLOSURE_SYSTEM_NT18,
598
- DISCLOSURE_SYSTEM_NT19,
599
- ],
514
+ disclosureSystems=NT_DISCLOSURE_SYSTEMS,
600
515
  )
601
516
  def rule_fr_nl_4_01(
602
517
  pluginData: PluginValidationDataExtension,
@@ -621,12 +536,7 @@ def rule_fr_nl_4_01(
621
536
 
622
537
  @validation(
623
538
  hook=ValidationHook.XBRL_FINALLY,
624
- disclosureSystems=[
625
- DISCLOSURE_SYSTEM_NT16,
626
- DISCLOSURE_SYSTEM_NT17,
627
- DISCLOSURE_SYSTEM_NT18,
628
- DISCLOSURE_SYSTEM_NT19,
629
- ],
539
+ disclosureSystems=NT_DISCLOSURE_SYSTEMS,
630
540
  )
631
541
  def rule_fr_nl_4_02(
632
542
  pluginData: PluginValidationDataExtension,
@@ -649,12 +559,7 @@ def rule_fr_nl_4_02(
649
559
 
650
560
  @validation(
651
561
  hook=ValidationHook.XBRL_FINALLY,
652
- disclosureSystems=[
653
- DISCLOSURE_SYSTEM_NT16,
654
- DISCLOSURE_SYSTEM_NT17,
655
- DISCLOSURE_SYSTEM_NT18,
656
- DISCLOSURE_SYSTEM_NT19,
657
- ],
562
+ disclosureSystems=NT_DISCLOSURE_SYSTEMS,
658
563
  )
659
564
  def rule_fr_nl_5_01(
660
565
  pluginData: PluginValidationDataExtension,
@@ -683,12 +588,7 @@ def rule_fr_nl_5_01(
683
588
 
684
589
  @validation(
685
590
  hook=ValidationHook.XBRL_FINALLY,
686
- disclosureSystems=[
687
- DISCLOSURE_SYSTEM_NT16,
688
- DISCLOSURE_SYSTEM_NT17,
689
- DISCLOSURE_SYSTEM_NT18,
690
- DISCLOSURE_SYSTEM_NT19,
691
- ],
591
+ disclosureSystems=NT_DISCLOSURE_SYSTEMS,
692
592
  )
693
593
  def rule_fr_nl_5_03(
694
594
  pluginData: PluginValidationDataExtension,
@@ -710,12 +610,7 @@ def rule_fr_nl_5_03(
710
610
 
711
611
  @validation(
712
612
  hook=ValidationHook.XBRL_FINALLY,
713
- disclosureSystems=[
714
- DISCLOSURE_SYSTEM_NT16,
715
- DISCLOSURE_SYSTEM_NT17,
716
- DISCLOSURE_SYSTEM_NT18,
717
- DISCLOSURE_SYSTEM_NT19,
718
- ],
613
+ disclosureSystems=NT_DISCLOSURE_SYSTEMS,
719
614
  )
720
615
  def rule_fr_nl_5_06(
721
616
  pluginData: PluginValidationDataExtension,
@@ -737,12 +632,7 @@ def rule_fr_nl_5_06(
737
632
 
738
633
  @validation(
739
634
  hook=ValidationHook.XBRL_FINALLY,
740
- disclosureSystems=[
741
- DISCLOSURE_SYSTEM_NT16,
742
- DISCLOSURE_SYSTEM_NT17,
743
- DISCLOSURE_SYSTEM_NT18,
744
- DISCLOSURE_SYSTEM_NT19,
745
- ],
635
+ disclosureSystems=NT_DISCLOSURE_SYSTEMS,
746
636
  )
747
637
  def rule_fr_nl_5_11(
748
638
  pluginData: PluginValidationDataExtension,
@@ -801,10 +691,10 @@ def rule_fr_nl_5_11(
801
691
  styleProperty = styleValues[0].strip()
802
692
  if styleProperty not in XHTML_ALLOWED_STYLES:
803
693
  invalidStyles.add(styleProperty)
804
- tag = qname(elt.tag).localName
694
+ tag = qname(cast(str, elt.tag)).localName
805
695
  parent = elt.getparent()
806
696
  if tag == 'li' and parent is not None:
807
- parentTag = qname(parent.tag).localName
697
+ parentTag = qname(cast(str, parent.tag)).localName
808
698
  if parentTag in XHTML_LIST_ITEM_TYPES:
809
699
  typeAttr = elt.get('type')
810
700
  if typeAttr not in XHTML_LIST_ITEM_TYPES[parentTag]:
@@ -840,12 +730,7 @@ def rule_fr_nl_5_11(
840
730
 
841
731
  @validation(
842
732
  hook=ValidationHook.XBRL_FINALLY,
843
- disclosureSystems=[
844
- DISCLOSURE_SYSTEM_NT16,
845
- DISCLOSURE_SYSTEM_NT17,
846
- DISCLOSURE_SYSTEM_NT18,
847
- DISCLOSURE_SYSTEM_NT19,
848
- ],
733
+ disclosureSystems=NT_DISCLOSURE_SYSTEMS,
849
734
  )
850
735
  def rule_fr_nl_6_01(
851
736
  pluginData: PluginValidationDataExtension,
@@ -29,9 +29,13 @@ from arelle.XbrlConst import parentChild, standardLabel
29
29
  from arelle.XmlValidateConst import VALID
30
30
 
31
31
  from ..DisclosureSystems import (
32
+ DISCLOSURE_SYSTEM_NL_INLINE_2025,
33
+ DISCLOSURE_SYSTEM_NL_INLINE_MULTI_TARGET,
32
34
  ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
33
35
  NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
34
36
  NL_INLINE_GAAP_OTHER_DISCLOSURE_SYSTEMS,
37
+ NL_INLINE_MULTI_TARGET_DISCLOSURE_SYSTEMS,
38
+ NL_INLINE_OTHER_DISCLOSURE_SYSTEMS,
35
39
  )
36
40
  from ..PluginValidationDataExtension import (
37
41
  ALLOWABLE_LANGUAGES,
@@ -1245,7 +1249,8 @@ def rule_nl_kvk_4_2_2_2(
1245
1249
  **kwargs: Any,
1246
1250
  ) -> Iterable[Validation]:
1247
1251
  """
1248
- 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.
1252
+ NL-KVK.4.2.2.2: Domain members MUST have domainItemType data type as defined in
1253
+ https://www.xbrl.org/dtr/type/2022-03-31/types.xsd or https://www.xbrl.org/dtr/type/2024-01-31/types.xsd
1249
1254
  """
1250
1255
  domainMembersWrongType = []
1251
1256
  domainMembers = pluginData.getDimensionalData(val.modelXbrl).domainMembers
@@ -1327,6 +1332,29 @@ def rule_nl_kvk_4_3_1_1(
1327
1332
  )
1328
1333
 
1329
1334
 
1335
+ @validation(
1336
+ hook=ValidationHook.XBRL_FINALLY,
1337
+ disclosureSystems=DISCLOSURE_SYSTEM_NL_INLINE_2025,
1338
+ )
1339
+ def rule_nl_kvk_4_3_1_2(
1340
+ pluginData: PluginValidationDataExtension,
1341
+ val: ValidateXbrl,
1342
+ *args: Any,
1343
+ **kwargs: Any,
1344
+ ) -> Iterable[Validation]:
1345
+ """
1346
+ NL-KVK.4.3.1.2: An extension element SHOULD be anchored to a taxonomy element with a compatible data type.
1347
+ """
1348
+ anchorData = pluginData.getAnchorData(val.modelXbrl)
1349
+ if len(anchorData.extConceptsNotAnchoredToSameDerivedType) > 0:
1350
+ yield Validation.warning(
1351
+ codes="NL.NL-KVK.4.3.1.2.incompatibleDataTypeAnchoringRelationship",
1352
+ msg=_("The extension and taxonomy concepts that participate in anchoring relationships must "
1353
+ "either have the same type or one concept type must derive from the other."),
1354
+ modelObject=anchorData.extConceptsNotAnchoredToSameDerivedType,
1355
+ )
1356
+
1357
+
1330
1358
  @validation(
1331
1359
  hook=ValidationHook.XBRL_FINALLY,
1332
1360
  disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
@@ -1722,23 +1750,27 @@ def rule_nl_kvk_4_4_6_1(
1722
1750
 
1723
1751
  @validation(
1724
1752
  hook=ValidationHook.XBRL_FINALLY,
1725
- disclosureSystems=NL_INLINE_GAAP_OTHER_DISCLOSURE_SYSTEMS,
1753
+ disclosureSystems=NL_INLINE_OTHER_DISCLOSURE_SYSTEMS,
1726
1754
  )
1727
- def rule_nl_kvk_5_1_3_1(
1755
+ def rule_nl_kvk_5_1_3_1_and_6_1_3_1(
1728
1756
  pluginData: PluginValidationDataExtension,
1729
1757
  val: ValidateXbrl,
1730
1758
  *args: Any,
1731
1759
  **kwargs: Any,
1732
1760
  ) -> Iterable[Validation]:
1733
1761
  """
1734
- NL-KVK.5.1.3.1: Validate that the imported taxonomy matches the KVK-specified entry point.
1762
+ NL-KVK.5.1.3.1 and NL-KVK.6.1.3.1: Validate that the imported taxonomy matches the KVK-specified entry point.
1735
1763
  - https://www.nltaxonomie.nl/kvk/2024-12-31/kvk-annual-report-other-gaap.xsd
1764
+ - https://www.nltaxonomie.nl/kvk/2025-12-31/kvk-annual-report-other.xsd
1736
1765
  """
1737
1766
  uris = {doc[0].uri for doc in val.modelXbrl.namespaceDocs.values()}
1738
1767
  matches = uris & EFFECTIVE_KVK_GAAP_OTHER_ENTRYPOINT_FILES
1739
1768
  if not matches:
1769
+ base_code = '5.1.3.1.requiredEntryPointOtherGaapNotReferenced'
1770
+ if str(val.disclosureSystem.name) in DISCLOSURE_SYSTEM_NL_INLINE_MULTI_TARGET:
1771
+ base_code = '6.1.3.1.requiredEntryPointOtherNotReferenced'
1740
1772
  yield Validation.error(
1741
- codes='NL.NL-KVK.5.1.3.1.requiredEntryPointOtherGaapNotReferenced',
1773
+ codes=f'NL.NL-KVK.{base_code}',
1742
1774
  msg=_('The extension taxonomy must import the entry point of the taxonomy files prepared by KVK.'),
1743
1775
  modelObject=val.modelXbrl.modelDocument
1744
1776
  )
@@ -1746,24 +1778,27 @@ def rule_nl_kvk_5_1_3_1(
1746
1778
 
1747
1779
  @validation(
1748
1780
  hook=ValidationHook.XBRL_FINALLY,
1749
- disclosureSystems=NL_INLINE_GAAP_OTHER_DISCLOSURE_SYSTEMS,
1781
+ disclosureSystems=NL_INLINE_OTHER_DISCLOSURE_SYSTEMS,
1750
1782
  )
1751
- def rule_nl_kvk_5_1_3_2(
1783
+ def rule_nl_kvk_5_1_3_2_and_6_1_3_2(
1752
1784
  pluginData: PluginValidationDataExtension,
1753
1785
  val: ValidateXbrl,
1754
1786
  *args: Any,
1755
1787
  **kwargs: Any,
1756
1788
  ) -> Iterable[Validation]:
1757
1789
  """
1758
- NL-KVK.5.1.3.2: The legal entity's report MUST import the applicable version of
1790
+ NL-KVK.5.1.3.2 and NL-KVK.6.1.3.2: The legal entity's report MUST import the applicable version of
1759
1791
  the taxonomy files prepared by KVK.
1760
1792
  """
1761
1793
  reportingPeriod = pluginData.getReportingPeriod(val.modelXbrl)
1762
1794
  uris = {doc[0].uri for doc in val.modelXbrl.namespaceDocs.values()}
1763
1795
  matches = uris & TAXONOMY_URLS_BY_YEAR.get(reportingPeriod or '', set())
1764
1796
  if not reportingPeriod or not matches:
1797
+ base_code = '5.1.3.2.incorrectVersionEntryPointOtherGaapReferenced'
1798
+ if str(val.disclosureSystem.name) in DISCLOSURE_SYSTEM_NL_INLINE_MULTI_TARGET:
1799
+ base_code = '6.1.3.2.incorrectVersionEntryPointOtherReferenced'
1765
1800
  yield Validation.error(
1766
- codes='NL.NL-KVK.5.1.3.2.incorrectVersionEntryPointOtherGaapReferenced',
1801
+ codes=f'NL.NL-KVK.{base_code}',
1767
1802
  msg=_('The report MUST import the applicable version of the taxonomy files prepared by KVK '
1768
1803
  'for the reported financial reporting period. Verify the taxonomy version and make sure '
1769
1804
  'that FinancialReportingPeriod are tagged correctly.'),
@@ -1771,25 +1806,103 @@ def rule_nl_kvk_5_1_3_2(
1771
1806
  )
1772
1807
 
1773
1808
 
1809
+ @validation(
1810
+ hook=ValidationHook.XBRL_FINALLY,
1811
+ disclosureSystems=NL_INLINE_MULTI_TARGET_DISCLOSURE_SYSTEMS,
1812
+ )
1813
+ def rule_nl_kvk_6_1_3_3(
1814
+ pluginData: PluginValidationDataExtension,
1815
+ val: ValidateXbrl,
1816
+ *args: Any,
1817
+ **kwargs: Any,
1818
+ ) -> Iterable[Validation]:
1819
+ """
1820
+ NL-KVK.6.1.3.3: The target attribute “filing-information” MUST be used for the content of the required elements
1821
+ for filing with the Business Register
1822
+ """
1823
+ targetElements = {elt.get("target") for elt in pluginData.getTargetElements(val.modelXbrl)}
1824
+ if len(targetElements) > 2 or 'filing-information' not in targetElements or 'default' not in targetElements:
1825
+ yield Validation.error(
1826
+ codes='NL.NL-KVK.6.1.3.3.requiredTargetAttributeNotUsed',
1827
+ msg=_('The target attribute `filing-information` MUST be used for the content of the required '
1828
+ 'elements for filing with the Business Register.'),
1829
+ modelObject=targetElements
1830
+ )
1831
+
1832
+
1833
+ @validation(
1834
+ hook=ValidationHook.XBRL_FINALLY,
1835
+ disclosureSystems=NL_INLINE_GAAP_OTHER_DISCLOSURE_SYSTEMS,
1836
+ )
1837
+ def rule_nl_kvk_7_1_4_2(
1838
+ pluginData: PluginValidationDataExtension,
1839
+ val: ValidateXbrl,
1840
+ *args: Any,
1841
+ **kwargs: Any,
1842
+ ) -> Iterable[Validation]:
1843
+ """
1844
+ NL-KVK.7.1.4.2: The concept kvk:AnnualReportOfForeignGroupHeadForExemptionUnderArticle403 MUST NOT be reported with
1845
+ a value of “False”.
1846
+ """
1847
+ factsInError = []
1848
+ articleFacts = val.modelXbrl.factsByQname.get(pluginData.AnnualReportOfForeignGroupHeadForExemptionUnderArticle403Qn, set())
1849
+ for fact in articleFacts:
1850
+ if fact is not None and fact.xValid >= VALID and fact.xValue == 'False':
1851
+ factsInError.append(fact)
1852
+ if len(factsInError) > 0:
1853
+ yield Validation.error(
1854
+ codes='NL.NL-KVK.7.1.4.2.reportedConcept403NotExpected',
1855
+ msg=_('A fact or facts tagged with `kvk:AnnualReportOfForeignGroupHeadForExemptionUnderArticle403` is incorrectly marked as False.'),
1856
+ modelObject=factsInError
1857
+ )
1858
+
1859
+
1860
+ @validation(
1861
+ hook=ValidationHook.XBRL_FINALLY,
1862
+ disclosureSystems=NL_INLINE_GAAP_OTHER_DISCLOSURE_SYSTEMS,
1863
+ )
1864
+ def rule_nl_kvk_7_2_1_2(
1865
+ pluginData: PluginValidationDataExtension,
1866
+ val: ValidateXbrl,
1867
+ *args: Any,
1868
+ **kwargs: Any,
1869
+ ) -> Iterable[Validation]:
1870
+ """
1871
+ NL-KVK.7.2.1.2: The concept kvk:AnnualReportOfForeignGroupHeadForExemptionUnderArticle408 MUST NOT be reported with
1872
+ a value of “False”.
1873
+ """
1874
+ factsInError = []
1875
+ articleFacts = val.modelXbrl.factsByQname.get(pluginData.AnnualReportOfForeignGroupHeadForExemptionUnderArticle408Qn, set())
1876
+ for fact in articleFacts:
1877
+ if fact is not None and fact.xValid >= VALID and fact.xValue == 'False':
1878
+ factsInError.append(fact)
1879
+ if len(factsInError) > 0:
1880
+ yield Validation.error(
1881
+ codes='NL.NL-KVK.7.2.1.2.reportedConcept408NotExpected',
1882
+ msg=_('A fact or facts tagged with `kvk:AnnualReportOfForeignGroupHeadForExemptionUnderArticle408` is incorrectly marked as False.'),
1883
+ modelObject=factsInError
1884
+ )
1885
+
1886
+
1774
1887
  @validation(
1775
1888
  hook=ValidationHook.XBRL_FINALLY,
1776
1889
  disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
1777
1890
  )
1778
- def rule_nl_kvk_6_1_1_1(
1891
+ def rule_nl_kvk_8_1_1_1(
1779
1892
  pluginData: PluginValidationDataExtension,
1780
1893
  val: ValidateXbrl,
1781
1894
  *args: Any,
1782
1895
  **kwargs: Any,
1783
1896
  ) -> Iterable[Validation]:
1784
1897
  """
1785
- NL-KVK.6.1.1.1: The size of the report package MUST NOT exceed 100 MB.
1898
+ NL-KVK.8.1.1.1: The size of the report package MUST NOT exceed 100 MB.
1786
1899
  """
1787
1900
  size = val.modelXbrl.fileSource.getBytesSize()
1788
1901
  if size is None:
1789
1902
  return # File size is not available, cannot validate
1790
1903
  if size > MAX_REPORT_PACKAGE_SIZE_MBS * 1_000_000: # Interpretting MB as megabytes (1,000,000 bytes)
1791
1904
  yield Validation.error(
1792
- codes='NL.NL-KVK.6.1.1.1.reportPackageMaximumSizeExceeded',
1905
+ codes='NL.NL-KVK.8.1.1.1.reportPackageMaximumSizeExceeded',
1793
1906
  msg=_('The size of the report package must not exceed %(maxSize)s MBs, size is %(size)s MBs.'),
1794
1907
  modelObject=val.modelXbrl, maxSize=MAX_REPORT_PACKAGE_SIZE_MBS, size=int(size/1000000)
1795
1908
  )
@@ -353,7 +353,9 @@ def rule_ros20(
353
353
  else:
354
354
  principal_currency_value = principal_currency_values.pop()
355
355
  monetary_facts = list(val.modelXbrl.factsByDatatype(False, qnXbrliMonetaryItemType))
356
- monetary_units = [list(fact.utrEntries)[0].unitId for fact in monetary_facts if fact.unit is not None]
356
+ monetary_units = [
357
+ list(fact.utrEntries)[0].unitId for fact in monetary_facts if fact.unit is not None and len(fact.utrEntries) > 0
358
+ ]
357
359
  unit_counts = Counter(monetary_units)
358
360
  principal_currency_value_count = unit_counts[principal_currency_value]
359
361
  for count in unit_counts.values():
@@ -9,7 +9,7 @@ References:
9
9
  """
10
10
  import os
11
11
  from arelle import ModelDocument, XmlUtil
12
- from arelle.ModelValue import qname, dateTime, DATE
12
+ from arelle.ModelValue import qname, dateTime, DATE, dateUnionEqual
13
13
  from arelle.ValidateDuplicateFacts import getDuplicateFactSets
14
14
  from arelle.ValidateXbrlCalcs import insignificantDigits
15
15
  from arelle.Version import authorLabel, copyrightLabel
@@ -17,6 +17,7 @@ from arelle.XbrlConst import xbrli, qnXbrliXbrl
17
17
  import regex as re
18
18
  import tinycss2.ast
19
19
  from collections import defaultdict
20
+ from arelle.XmlValidate import VALID
20
21
 
21
22
  from .ValidateUK import ValidateUK
22
23
 
@@ -201,9 +202,9 @@ def validateXbrlFinally(val, *args, **kwargs):
201
202
  scheme, identifier = c1.entityIdentifier
202
203
  if scheme == "http://www.companieshouse.gov.uk/":
203
204
  companyReferenceNumberContexts[identifier].append(c1.id)
204
- atLeastOneFacts = {}
205
+ atLeastOneFacts = defaultdict(set)
205
206
  uniqueFacts = {} # key = (qname, context hash, unit hash, lang)
206
- mandatoryFacts = {}
207
+ mandatoryFacts = defaultdict(set)
207
208
  mandatoryGDV = defaultdict(set)
208
209
  factForConceptContextUnitHash = defaultdict(list)
209
210
  hasCompaniesHouseContext = any(cntx.entityIdentifier[0] == "http://www.companieshouse.gov.uk/"
@@ -250,12 +251,12 @@ def validateXbrlFinally(val, *args, **kwargs):
250
251
  factNamespaceURI = f.qname.namespaceURI
251
252
  factLocalName = f.qname.localName
252
253
  if factLocalName in MANDATORY_ITEMS[val.txmyType]:
253
- mandatoryFacts[factLocalName] = f
254
+ mandatoryFacts[factLocalName].add(f)
254
255
  if val.txmyType in MUST_HAVE_ONE_ITEM and factLocalName in MUST_HAVE_ONE_ITEM[val.txmyType]:
255
- atLeastOneFacts[factLocalName] = f
256
+ atLeastOneFacts[factLocalName].add(f)
256
257
  if factLocalName == "UKCompaniesHouseRegisteredNumber" and val.isAccounts:
257
258
  if hasCompaniesHouseContext:
258
- mandatoryFacts[factLocalName] = f
259
+ mandatoryFacts[factLocalName].add(f)
259
260
  for _cntx in contextsUsed:
260
261
  _scheme, _identifier = _cntx.entityIdentifier
261
262
  if _scheme == "http://www.companieshouse.gov.uk/" and f.xValue != _identifier:
@@ -326,17 +327,72 @@ def validateXbrlFinally(val, *args, **kwargs):
326
327
  checkFacts(modelXbrl.facts)
327
328
 
328
329
  if val.isAccounts:
329
- _missingItems = MANDATORY_ITEMS[val.txmyType] - mandatoryFacts.keys()
330
- if hasCompaniesHouseContext and "UKCompaniesHouseRegisteredNumber" not in mandatoryFacts:
331
- _missingItems.add("UKCompaniesHouseRegisteredNumber")
330
+ startDate = None
331
+ endDate = None
332
+ _missingItems = []
333
+ for fact in mandatoryFacts.get('EndDateForPeriodCoveredByReport', set()):
334
+ if (fact is not None and
335
+ fact.xValid >= VALID and
336
+ fact.context is not None and
337
+ fact.context.isInstantPeriod and
338
+ dateUnionEqual(fact.context.instantDate, fact.xValue)):
339
+ endDate = fact.xValue
340
+ break
341
+ else:
342
+ _missingItems.append('EndDateForPeriodCoveredByReport')
343
+
344
+ for fact in mandatoryFacts.get('StartDateForPeriodCoveredByReport', set()):
345
+ if (fact is not None and
346
+ fact.xValid >= VALID and
347
+ fact.context is not None and
348
+ fact.context.isInstantPeriod and
349
+ dateUnionEqual(fact.context.instantDate, endDate)):
350
+ startDate = fact.xValue
351
+ break
352
+ else:
353
+ _missingItems.append('StartDateForPeriodCoveredByReport')
354
+
355
+ if startDate is not None and endDate is not None:
356
+ mandatoryConceptsToCheck = MANDATORY_ITEMS[val.txmyType]
357
+ if hasCompaniesHouseContext:
358
+ mandatoryConceptsToCheck = MANDATORY_ITEMS[val.txmyType] | {"UKCompaniesHouseRegisteredNumber"}
359
+
360
+ for mandatoryConcept in mandatoryConceptsToCheck:
361
+ if mandatoryConcept in ['StartDateForPeriodCoveredByReport', 'EndDateForPeriodCoveredByReport']:
362
+ continue
363
+ foundFact = False
364
+ for fact in mandatoryFacts.get(mandatoryConcept, set()):
365
+ if (fact is not None and fact.context is not None and fact.xValid >= VALID and
366
+ ((fact.context.isInstantPeriod and dateUnionEqual(fact.context.instantDate, endDate) or
367
+ (fact.context.isStartEndPeriod and dateUnionEqual(fact.context.startDatetime, startDate) and
368
+ dateUnionEqual(fact.context.endDate, endDate, instantEndDate=True))))):
369
+ foundFact = True
370
+ break
371
+ if not foundFact:
372
+ _missingItems.append(mandatoryConcept)
373
+
332
374
  if _missingItems:
333
375
  modelXbrl.error("JFCVC.3312",
334
- _("Facts are MANDATORY: %(missingItems)s"),
376
+ _("The following mandatory concepts are either not tagged on a fact or are tagged on facts that "
377
+ "have contexts that do not align with the dates as reported in "
378
+ "'StartDateForPeriodCoveredByReport' and 'EndDateForPeriodCoveredByReport': %(missingItems)s"),
335
379
  modelObject=modelXbrl, missingItems=", ".join(sorted(_missingItems)))
336
- if not atLeastOneFacts and val.txmyType in MUST_HAVE_ONE_ITEM:
337
- modelXbrl.error("JFCVC.3312.atLeastOne",
338
- _("At least one of the facts is MANDATORY: %(missingItems)s"),
339
- modelObject=modelXbrl, missingItems=", ".join(sorted(MUST_HAVE_ONE_ITEM[val.txmyType])))
380
+
381
+ if startDate is not None and endDate is not None and val.txmyType in MUST_HAVE_ONE_ITEM:
382
+ foundFact = False
383
+ for mustHaveOneConcept in MUST_HAVE_ONE_ITEM[val.txmyType]:
384
+ for fact in atLeastOneFacts.get(mustHaveOneConcept, set()):
385
+ if (fact is not None and fact.context is not None and fact.xValid >= VALID and
386
+ ((fact.context.isInstantPeriod and dateUnionEqual(fact.context.instantDate, endDate) or
387
+ (fact.context.isStartEndPeriod and dateUnionEqual(fact.context.startDatetime, startDate) and
388
+ dateUnionEqual(fact.context.endDate, endDate, instantEndDate=True))))):
389
+ foundFact = True
390
+ break
391
+
392
+ if not foundFact:
393
+ modelXbrl.error("JFCVC.3312.atLeastOne",
394
+ _("At least one of the facts is MANDATORY: %(missingItems)s"),
395
+ modelObject=modelXbrl, missingItems=", ".join(sorted(MUST_HAVE_ONE_ITEM[val.txmyType])))
340
396
 
341
397
  ''' removed with JFCVC v4.0 2020-06-09
342
398
  f = mandatoryFacts.get("StartDateForPeriodCoveredByReport")