arelle-release 2.37.22__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.

@@ -3,36 +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
 
9
- from arelle.ModelDtsObject import ModelLink
13
+ from lxml.etree import Element
14
+
15
+ from arelle.ModelDtsObject import ModelLink, ModelResource, ModelType
10
16
  from arelle.ModelInstanceObject import ModelInlineFact
11
17
  from arelle.ModelObject import ModelObject
12
18
  from arelle.PrototypeDtsObject import PrototypeObject
13
19
  from arelle.ValidateDuplicateFacts import getDuplicateFactSets
14
20
  from arelle.XmlValidateConst import VALID
15
- from collections.abc import Iterable
16
- from typing import Any, cast, TYPE_CHECKING
17
21
 
18
- from arelle import XmlUtil, XbrlConst, ModelDocument
22
+ from arelle import XbrlConst, XmlUtil
19
23
  from arelle.ValidateXbrl import ValidateXbrl
20
24
  from arelle.typing import TypeGetText
21
25
  from arelle.utils.PluginHooks import ValidationHook
22
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
23
29
  from arelle.utils.validate.Validation import Validation
24
30
  from arelle.ValidateDuplicateFacts import getHashEquivalentFactGroups, getAspectEqualFacts
25
31
  from arelle.utils.validate.ValidationUtil import etreeIterWithDepth
26
32
  from ..DisclosureSystems import (ALL_NL_INLINE_DISCLOSURE_SYSTEMS, NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
27
33
  NL_INLINE_GAAP_OTHER_DISCLOSURE_SYSTEMS)
28
34
  from ..LinkbaseType import LinkbaseType
29
- from ..PluginValidationDataExtension import (PluginValidationDataExtension, ALLOWABLE_LANGUAGES,
30
- DEFAULT_MEMBER_ROLE_URI, DISALLOWED_IXT_NAMESPACES,
31
- EFFECTIVE_KVK_GAAP_IFRS_ENTRYPOINT_FILES,
32
- EFFECTIVE_KVK_GAAP_OTHER_ENTRYPOINT_FILES,
33
- MAX_REPORT_PACKAGE_SIZE_MBS, TAXONOMY_URLS_BY_YEAR,
34
- XBRLI_IDENTIFIER_PATTERN, XBRLI_IDENTIFIER_SCHEMA,
35
- QN_DOMAIN_ITEM_TYPES)
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
+ )
36
50
 
37
51
  if TYPE_CHECKING:
38
52
  from arelle.ModelXbrl import ModelXbrl
@@ -69,7 +83,7 @@ def rule_nl_kvk_3_1_1_1(
69
83
  if not XBRLI_IDENTIFIER_PATTERN.match(entityId[1]):
70
84
  yield Validation.error(
71
85
  codes='NL.NL-KVK-RTS_Annex_IV_Par_2_G3-1-1_1.invalidIdentifierFormat',
72
- 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. '
73
87
  'Additionally the first two digits must not be "00".'),
74
88
  modelObject = val.modelXbrl
75
89
  )
@@ -94,7 +108,7 @@ def rule_nl_kvk_3_1_1_2(
94
108
  if XBRLI_IDENTIFIER_SCHEMA != entityId[0]:
95
109
  yield Validation.error(
96
110
  codes='NL.NL-KVK-RTS_Annex_IV_Par_2_G3-1-1_2.invalidIdentifier',
97
- 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. '
98
112
  'This should be "http://www.kvk.nl/kvk-id".'),
99
113
  modelObject = val.modelXbrl
100
114
  )
@@ -367,12 +381,34 @@ def rule_nl_kvk_3_2_7_1 (
367
381
  if len(improperlyEscapedFacts) >0:
368
382
  yield Validation.error(
369
383
  codes='NL.NL-KVK.3.2.7.1.improperApplicationOfEscapeAttribute',
370
- 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", '
371
385
  'while other data types (e.g., xbrli:stringItemType) are assigned @escape="false".'),
372
386
  modelObject = improperlyEscapedFacts
373
387
  )
374
388
 
375
389
 
390
+ @validation(
391
+ hook=ValidationHook.XBRL_FINALLY,
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
+
376
412
  @validation(
377
413
  hook=ValidationHook.XBRL_FINALLY,
378
414
  disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
@@ -574,6 +610,72 @@ def rule_nl_kvk_3_4_2_1 (
574
610
  )
575
611
 
576
612
 
613
+ @validation(
614
+ hook=ValidationHook.XBRL_FINALLY,
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
+
577
679
  @validation(
578
680
  hook=ValidationHook.XBRL_FINALLY,
579
681
  disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
@@ -585,7 +687,7 @@ def rule_nl_kvk_3_5_2_1(
585
687
  **kwargs: Any,
586
688
  ) -> Iterable[Validation]:
587
689
  """
588
- 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.
589
691
  """
590
692
  factsWithoutLang = []
591
693
  for fact in val.modelXbrl.facts:
@@ -598,7 +700,7 @@ def rule_nl_kvk_3_5_2_1(
598
700
  if len(factsWithoutLang) > 0:
599
701
  yield Validation.error(
600
702
  codes='NL.NL-KVK.3.5.2.1.undefinedLanguageForTextFact',
601
- 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."),
602
704
  modelObject=factsWithoutLang
603
705
  )
604
706
 
@@ -973,7 +1075,7 @@ def rule_nl_kvk_4_1_2_2(
973
1075
  **kwargs: Any,
974
1076
  ) -> Iterable[Validation]:
975
1077
  """
976
- 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
977
1079
  the taxonomy files prepared by KVK.
978
1080
  """
979
1081
  reportingPeriod = pluginData.getReportingPeriod(val.modelXbrl)
@@ -1143,7 +1245,7 @@ def rule_nl_kvk_4_2_2_2(
1143
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.
1144
1246
  """
1145
1247
  domainMembersWrongType = []
1146
- domainMembers = pluginData.getDomainMembers(val.modelXbrl)
1248
+ domainMembers = pluginData.getDimensionalData(val.modelXbrl).domainMembers
1147
1249
  extensionData = pluginData.getExtensionData(val.modelXbrl)
1148
1250
  for concept in extensionData.extensionConcepts:
1149
1251
  if concept.isDomainMember and concept in domainMembers and concept.typeQname not in QN_DOMAIN_ITEM_TYPES:
@@ -1176,11 +1278,79 @@ def rule_nl_kvk_4_2_3_1(
1176
1278
  typedDims.append(concept)
1177
1279
  if len(typedDims) > 0:
1178
1280
  yield Validation.error(
1179
- codes='NL.NL-KVK.4.3.2.1.typedDimensionDefinitionInExtensionTaxonomy',
1281
+ codes='NL.NL-KVK.4.2.3.1.typedDimensionDefinitionInExtensionTaxonomy',
1180
1282
  modelObject=typedDims,
1181
1283
  msg=_('Typed dimensions are not allowed in the extension taxonomy. Update to remove the typed dimension.'))
1182
1284
 
1183
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
+
1184
1354
  @validation(
1185
1355
  hook=ValidationHook.XBRL_FINALLY,
1186
1356
  disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
@@ -1248,7 +1418,7 @@ def rule_nl_kvk_4_4_2_2(
1248
1418
  ) -> Iterable[Validation]:
1249
1419
  """
1250
1420
  NL-KVK.4.4.2.2: Hypercubes appearing as target of definition arc with
1251
- 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".
1252
1422
  """
1253
1423
  errors = []
1254
1424
  extensionData = pluginData.getExtensionData(val.modelXbrl)
@@ -1276,7 +1446,7 @@ def rule_nl_kvk_4_4_2_3(
1276
1446
  ) -> Iterable[Validation]:
1277
1447
  """
1278
1448
  NL-KVK.4.4.2.3: Hypercubes appearing as target of definition arc with
1279
- 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".
1280
1450
  """
1281
1451
  errors = []
1282
1452
  extensionData = pluginData.getExtensionData(val.modelXbrl)
@@ -1292,6 +1462,35 @@ def rule_nl_kvk_4_4_2_3(
1292
1462
  )
1293
1463
 
1294
1464
 
1465
+ @validation(
1466
+ hook=ValidationHook.XBRL_FINALLY,
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
+
1295
1494
  @validation(
1296
1495
  hook=ValidationHook.XBRL_FINALLY,
1297
1496
  disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
@@ -1363,6 +1562,156 @@ def rule_nl_kvk_4_4_3_2(
1363
1562
  )
1364
1563
 
1365
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
+
1366
1715
  @validation(
1367
1716
  hook=ValidationHook.XBRL_FINALLY,
1368
1717
  disclosureSystems=NL_INLINE_GAAP_OTHER_DISCLOSURE_SYSTEMS,
@@ -1398,7 +1747,7 @@ def rule_nl_kvk_5_1_3_2(
1398
1747
  **kwargs: Any,
1399
1748
  ) -> Iterable[Validation]:
1400
1749
  """
1401
- NL-KVK.5.1.3.2: The legal entitys report MUST import the applicable version of
1750
+ NL-KVK.5.1.3.2: The legal entity's report MUST import the applicable version of
1402
1751
  the taxonomy files prepared by KVK.
1403
1752
  """
1404
1753
  reportingPeriod = pluginData.getReportingPeriod(val.modelXbrl)
@@ -1438,3 +1787,89 @@ def rule_nl_kvk_6_1_1_1(
1438
1787
  msg=_('The size of the report package must not exceed %(maxSize)s MBs, size is %(size)s MBs.'),
1439
1788
  modelObject=val.modelXbrl, maxSize=MAX_REPORT_PACKAGE_SIZE_MBS, size=int(_size/1000000)
1440
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
+ )
@@ -1,4 +1,3 @@
1
- from operator import truediv
2
1
  from typing import Any, Collection
3
2
 
4
3
  from arelle.XbrlConst import xhtml
@@ -92,9 +91,7 @@ def _hasEventAttributes(elt: Any, attributes: Collection[str]) -> bool:
92
91
  def containsScriptMarkers(elt: Any) -> Any:
93
92
  _xhtmlNs = "{{{}}}".format(xhtml)
94
93
  _xhtmlNsLen = len(_xhtmlNs)
95
- eltTag = elt.tag
96
- if eltTag.startswith(_xhtmlNs):
97
- eltTag = eltTag[_xhtmlNsLen:]
94
+ eltTag = elt.tag.removeprefix(_xhtmlNs)
98
95
  if ((eltTag in ("object", "script")) or
99
96
  (eltTag == "a" and "javascript:" in elt.get("href","")) or
100
97
  (eltTag == "img" and "javascript:" in elt.get("src","")) or