arelle-release 2.37.22__py3-none-any.whl → 2.37.25__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of arelle-release might be problematic. Click here for more details.
- arelle/ModelRelationshipSet.py +3 -0
- arelle/Updater.py +7 -3
- arelle/ValidateDuplicateFacts.py +13 -7
- arelle/XbrlConst.py +22 -0
- arelle/_version.py +2 -2
- arelle/plugin/validate/DBA/rules/fr.py +10 -10
- arelle/plugin/validate/DBA/rules/th.py +1 -1
- arelle/plugin/validate/ESEF/ESEF_Current/ValidateXbrlFinally.py +21 -20
- arelle/plugin/validate/NL/LinkbaseType.py +17 -0
- arelle/plugin/validate/NL/PluginValidationDataExtension.py +155 -4
- arelle/plugin/validate/NL/resources/config.xml +6 -0
- arelle/plugin/validate/NL/rules/fr_kvk.py +1 -1
- arelle/plugin/validate/NL/rules/nl_kvk.py +656 -22
- arelle/utils/validate/DetectScriptsInXhtml.py +1 -4
- arelle/utils/validate/ESEFImage.py +274 -0
- {arelle_release-2.37.22.dist-info → arelle_release-2.37.25.dist-info}/METADATA +1 -1
- {arelle_release-2.37.22.dist-info → arelle_release-2.37.25.dist-info}/RECORD +27 -27
- tests/integration_tests/validation/conformance_suite_configurations/efm_current.py +2 -2
- tests/integration_tests/validation/conformance_suite_configurations/nl_inline_2024.py +52 -28
- tests/integration_tests/validation/conformance_suite_configurations/nl_inline_2024_gaap_other.py +10 -0
- tests/resources/conformance_suites_timing/efm_current.json +8499 -8583
- tests/unit_tests/arelle/plugin/validate/ESEF/ESEF_Current/test_validate_css_url.py +10 -2
- tests/unit_tests/arelle/test_updater.py +43 -14
- arelle/plugin/validate/ESEF/ESEF_Current/Image.py +0 -213
- {arelle_release-2.37.22.dist-info → arelle_release-2.37.25.dist-info}/WHEEL +0 -0
- {arelle_release-2.37.22.dist-info → arelle_release-2.37.25.dist-info}/entry_points.txt +0 -0
- {arelle_release-2.37.22.dist-info → arelle_release-2.37.25.dist-info}/licenses/LICENSE.md +0 -0
- {arelle_release-2.37.22.dist-info → arelle_release-2.37.25.dist-info}/top_level.txt +0 -0
|
@@ -3,36 +3,51 @@ See COPYRIGHT.md for copyright information.
|
|
|
3
3
|
"""
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
|
+
import re
|
|
7
|
+
from collections import defaultdict
|
|
8
|
+
from collections.abc import Iterable
|
|
6
9
|
from datetime import date
|
|
10
|
+
from typing import Any, cast, TYPE_CHECKING
|
|
11
|
+
|
|
7
12
|
import zipfile
|
|
8
13
|
|
|
9
|
-
from
|
|
14
|
+
from lxml.etree import Element
|
|
15
|
+
|
|
16
|
+
from arelle.ModelDtsObject import ModelConcept, ModelLink, ModelResource, ModelType
|
|
10
17
|
from arelle.ModelInstanceObject import ModelInlineFact
|
|
11
18
|
from arelle.ModelObject import ModelObject
|
|
12
19
|
from arelle.PrototypeDtsObject import PrototypeObject
|
|
13
20
|
from arelle.ValidateDuplicateFacts import getDuplicateFactSets
|
|
14
21
|
from arelle.XmlValidateConst import VALID
|
|
15
|
-
from collections.abc import Iterable
|
|
16
|
-
from typing import Any, cast, TYPE_CHECKING
|
|
17
22
|
|
|
18
|
-
from arelle import
|
|
23
|
+
from arelle import XbrlConst, XmlUtil, ModelDocument
|
|
19
24
|
from arelle.ValidateXbrl import ValidateXbrl
|
|
20
25
|
from arelle.typing import TypeGetText
|
|
21
26
|
from arelle.utils.PluginHooks import ValidationHook
|
|
22
27
|
from arelle.utils.validate.Decorator import validation
|
|
28
|
+
from arelle.utils.validate.DetectScriptsInXhtml import containsScriptMarkers
|
|
29
|
+
from arelle.utils.validate.ESEFImage import ImageValidationParameters, validateImage
|
|
23
30
|
from arelle.utils.validate.Validation import Validation
|
|
24
31
|
from arelle.ValidateDuplicateFacts import getHashEquivalentFactGroups, getAspectEqualFacts
|
|
25
32
|
from arelle.utils.validate.ValidationUtil import etreeIterWithDepth
|
|
26
33
|
from ..DisclosureSystems import (ALL_NL_INLINE_DISCLOSURE_SYSTEMS, NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
|
|
27
34
|
NL_INLINE_GAAP_OTHER_DISCLOSURE_SYSTEMS)
|
|
28
35
|
from ..LinkbaseType import LinkbaseType
|
|
29
|
-
from ..PluginValidationDataExtension import (
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
from ..PluginValidationDataExtension import (
|
|
37
|
+
PluginValidationDataExtension,
|
|
38
|
+
ALLOWABLE_LANGUAGES,
|
|
39
|
+
DEFAULT_MEMBER_ROLE_URI,
|
|
40
|
+
DISALLOWED_IXT_NAMESPACES,
|
|
41
|
+
EFFECTIVE_KVK_GAAP_IFRS_ENTRYPOINT_FILES,
|
|
42
|
+
EFFECTIVE_KVK_GAAP_OTHER_ENTRYPOINT_FILES,
|
|
43
|
+
MAX_REPORT_PACKAGE_SIZE_MBS,
|
|
44
|
+
NON_DIMENSIONALIZED_LINE_ITEM_LINKROLES,
|
|
45
|
+
QN_DOMAIN_ITEM_TYPES,
|
|
46
|
+
SUPPORTED_IMAGE_TYPES_BY_IS_FILE,
|
|
47
|
+
TAXONOMY_URLS_BY_YEAR,
|
|
48
|
+
XBRLI_IDENTIFIER_PATTERN,
|
|
49
|
+
XBRLI_IDENTIFIER_SCHEMA,
|
|
50
|
+
)
|
|
36
51
|
|
|
37
52
|
if TYPE_CHECKING:
|
|
38
53
|
from arelle.ModelXbrl import ModelXbrl
|
|
@@ -40,6 +55,8 @@ if TYPE_CHECKING:
|
|
|
40
55
|
|
|
41
56
|
_: TypeGetText
|
|
42
57
|
|
|
58
|
+
DOCTYPE_XHTML_PATTERN = re.compile(r"^<!(?:DOCTYPE\s+)\s*html(?:PUBLIC\s+)?(?:.*-//W3C//DTD\s+(X?HTML)\s)?.*>$", re.IGNORECASE)
|
|
59
|
+
|
|
43
60
|
|
|
44
61
|
def _getReportingPeriodDateValue(modelXbrl: ModelXbrl, qname: QName) -> date | None:
|
|
45
62
|
facts = modelXbrl.factsByQname.get(qname)
|
|
@@ -69,7 +86,7 @@ def rule_nl_kvk_3_1_1_1(
|
|
|
69
86
|
if not XBRLI_IDENTIFIER_PATTERN.match(entityId[1]):
|
|
70
87
|
yield Validation.error(
|
|
71
88
|
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.'
|
|
89
|
+
msg=_('xbrli:identifier content to match KVK number format that must consist of 8 consecutive digits. '
|
|
73
90
|
'Additionally the first two digits must not be "00".'),
|
|
74
91
|
modelObject = val.modelXbrl
|
|
75
92
|
)
|
|
@@ -94,7 +111,7 @@ def rule_nl_kvk_3_1_1_2(
|
|
|
94
111
|
if XBRLI_IDENTIFIER_SCHEMA != entityId[0]:
|
|
95
112
|
yield Validation.error(
|
|
96
113
|
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.'
|
|
114
|
+
msg=_('The scheme attribute of the xbrli:identifier does not match the required content. '
|
|
98
115
|
'This should be "http://www.kvk.nl/kvk-id".'),
|
|
99
116
|
modelObject = val.modelXbrl
|
|
100
117
|
)
|
|
@@ -367,12 +384,34 @@ def rule_nl_kvk_3_2_7_1 (
|
|
|
367
384
|
if len(improperlyEscapedFacts) >0:
|
|
368
385
|
yield Validation.error(
|
|
369
386
|
codes='NL.NL-KVK.3.2.7.1.improperApplicationOfEscapeAttribute',
|
|
370
|
-
msg=_('Ensure that any block-tagged facts of type textBlockItemType are assigned @escape="true",'
|
|
387
|
+
msg=_('Ensure that any block-tagged facts of type textBlockItemType are assigned @escape="true", '
|
|
371
388
|
'while other data types (e.g., xbrli:stringItemType) are assigned @escape="false".'),
|
|
372
389
|
modelObject = improperlyEscapedFacts
|
|
373
390
|
)
|
|
374
391
|
|
|
375
392
|
|
|
393
|
+
@validation(
|
|
394
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
395
|
+
disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
|
|
396
|
+
)
|
|
397
|
+
def rule_nl_kvk_3_2_8_1(
|
|
398
|
+
pluginData: PluginValidationDataExtension,
|
|
399
|
+
val: ValidateXbrl,
|
|
400
|
+
*args: Any,
|
|
401
|
+
**kwargs: Any,
|
|
402
|
+
) -> Iterable[Validation]:
|
|
403
|
+
"""
|
|
404
|
+
NL-KVK.3.2.8.1: Include unique @id attribute for each tagged fact
|
|
405
|
+
"""
|
|
406
|
+
errors = {fact for fact in val.modelXbrl.facts if not fact.id}
|
|
407
|
+
if len(errors) > 0:
|
|
408
|
+
yield Validation.warning(
|
|
409
|
+
codes='NL.NL-KVK.3.2.8.1',
|
|
410
|
+
msg=_('All facts should include an id attribute'),
|
|
411
|
+
modelObject=errors
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
|
|
376
415
|
@validation(
|
|
377
416
|
hook=ValidationHook.XBRL_FINALLY,
|
|
378
417
|
disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
|
|
@@ -574,6 +613,72 @@ def rule_nl_kvk_3_4_2_1 (
|
|
|
574
613
|
)
|
|
575
614
|
|
|
576
615
|
|
|
616
|
+
@validation(
|
|
617
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
618
|
+
disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
|
|
619
|
+
)
|
|
620
|
+
def rule_nl_kvk_3_5_1_1_non_img (
|
|
621
|
+
pluginData: PluginValidationDataExtension,
|
|
622
|
+
val: ValidateXbrl,
|
|
623
|
+
*args: Any,
|
|
624
|
+
**kwargs: Any,
|
|
625
|
+
) -> Iterable[Validation]:
|
|
626
|
+
"""
|
|
627
|
+
NL-KVK.3.5.1.1: Resources embedded or referenced by the XHTML document and its inline XBRL MUST NOT contain executable code.
|
|
628
|
+
"""
|
|
629
|
+
|
|
630
|
+
executableElements = []
|
|
631
|
+
for ixdsHtmlRootElt in val.modelXbrl.ixdsHtmlElements:
|
|
632
|
+
for elt in ixdsHtmlRootElt.iter(Element):
|
|
633
|
+
if containsScriptMarkers(elt):
|
|
634
|
+
executableElements.append(elt)
|
|
635
|
+
if executableElements:
|
|
636
|
+
yield Validation.error(
|
|
637
|
+
codes='NL.NL-KVK.3.5.1.1.executableCodePresent',
|
|
638
|
+
msg=_("Resources embedded or referenced by the XHTML document and its inline XBRL MUST NOT contain executable code."),
|
|
639
|
+
modelObject=executableElements,
|
|
640
|
+
)
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
@validation(
|
|
644
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
645
|
+
disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
|
|
646
|
+
)
|
|
647
|
+
def rule_nl_kvk_3_5_1_img (
|
|
648
|
+
pluginData: PluginValidationDataExtension,
|
|
649
|
+
val: ValidateXbrl,
|
|
650
|
+
*args: Any,
|
|
651
|
+
**kwargs: Any,
|
|
652
|
+
) -> Iterable[Validation]:
|
|
653
|
+
"""
|
|
654
|
+
NL-KVK.3.5.1.1: Inline XBRL images MUST NOT contain executable code.
|
|
655
|
+
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.
|
|
656
|
+
NL-KVK.3.5.1.3: File type inferred from file signature does not match the data URL media subtype (MIME subtype).
|
|
657
|
+
NL-KVK.3.5.1.4: File type inferred from file signature does not match the file extension.
|
|
658
|
+
NL-KVK.3.5.1.5: Images included in the XHTML document MUST be saved in PNG, GIF, SVG or JPG/JPEG formats.
|
|
659
|
+
"""
|
|
660
|
+
|
|
661
|
+
imageValidationParameters = ImageValidationParameters.from_non_esef(
|
|
662
|
+
checkMinExternalResourceSize=False,
|
|
663
|
+
missingMimeTypeIsIncorrect=False,
|
|
664
|
+
recommendBase64EncodingEmbeddedImages=False,
|
|
665
|
+
supportedImgTypes=SUPPORTED_IMAGE_TYPES_BY_IS_FILE,
|
|
666
|
+
)
|
|
667
|
+
for ixdsHtmlRootElt in val.modelXbrl.ixdsHtmlElements:
|
|
668
|
+
for elt in ixdsHtmlRootElt.iter((f'{{{XbrlConst.xhtml}}}img', '{http://www.w3.org/2000/svg}svg')):
|
|
669
|
+
src = elt.get('src', '').strip()
|
|
670
|
+
evaluatedMsg = _('On line {line}, "alt" attribute value: "{alt}"').format(line=elt.sourceline, alt=elt.get('alt'))
|
|
671
|
+
yield from validateImage(
|
|
672
|
+
elt.modelDocument.baseForElement(elt),
|
|
673
|
+
src,
|
|
674
|
+
val.modelXbrl,
|
|
675
|
+
val,
|
|
676
|
+
elt,
|
|
677
|
+
evaluatedMsg,
|
|
678
|
+
imageValidationParameters,
|
|
679
|
+
)
|
|
680
|
+
|
|
681
|
+
|
|
577
682
|
@validation(
|
|
578
683
|
hook=ValidationHook.XBRL_FINALLY,
|
|
579
684
|
disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
|
|
@@ -585,7 +690,7 @@ def rule_nl_kvk_3_5_2_1(
|
|
|
585
690
|
**kwargs: Any,
|
|
586
691
|
) -> Iterable[Validation]:
|
|
587
692
|
"""
|
|
588
|
-
NL-KVK.3.5.2.1: Each tagged text fact MUST have the
|
|
693
|
+
NL-KVK.3.5.2.1: Each tagged text fact MUST have the 'xml:lang' attribute assigned or inherited.
|
|
589
694
|
"""
|
|
590
695
|
factsWithoutLang = []
|
|
591
696
|
for fact in val.modelXbrl.facts:
|
|
@@ -598,7 +703,7 @@ def rule_nl_kvk_3_5_2_1(
|
|
|
598
703
|
if len(factsWithoutLang) > 0:
|
|
599
704
|
yield Validation.error(
|
|
600
705
|
codes='NL.NL-KVK.3.5.2.1.undefinedLanguageForTextFact',
|
|
601
|
-
msg=_(
|
|
706
|
+
msg=_("Each tagged text fact MUST have the 'xml:lang' attribute assigned or inherited."),
|
|
602
707
|
modelObject=factsWithoutLang
|
|
603
708
|
)
|
|
604
709
|
|
|
@@ -973,7 +1078,7 @@ def rule_nl_kvk_4_1_2_2(
|
|
|
973
1078
|
**kwargs: Any,
|
|
974
1079
|
) -> Iterable[Validation]:
|
|
975
1080
|
"""
|
|
976
|
-
NL-KVK.4.1.2.2: The legal entity
|
|
1081
|
+
NL-KVK.4.1.2.2: The legal entity's extension taxonomy MUST import the applicable version of
|
|
977
1082
|
the taxonomy files prepared by KVK.
|
|
978
1083
|
"""
|
|
979
1084
|
reportingPeriod = pluginData.getReportingPeriod(val.modelXbrl)
|
|
@@ -1143,7 +1248,7 @@ def rule_nl_kvk_4_2_2_2(
|
|
|
1143
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.
|
|
1144
1249
|
"""
|
|
1145
1250
|
domainMembersWrongType = []
|
|
1146
|
-
domainMembers = pluginData.
|
|
1251
|
+
domainMembers = pluginData.getDimensionalData(val.modelXbrl).domainMembers
|
|
1147
1252
|
extensionData = pluginData.getExtensionData(val.modelXbrl)
|
|
1148
1253
|
for concept in extensionData.extensionConcepts:
|
|
1149
1254
|
if concept.isDomainMember and concept in domainMembers and concept.typeQname not in QN_DOMAIN_ITEM_TYPES:
|
|
@@ -1176,11 +1281,79 @@ def rule_nl_kvk_4_2_3_1(
|
|
|
1176
1281
|
typedDims.append(concept)
|
|
1177
1282
|
if len(typedDims) > 0:
|
|
1178
1283
|
yield Validation.error(
|
|
1179
|
-
codes='NL.NL-KVK.4.3.
|
|
1284
|
+
codes='NL.NL-KVK.4.2.3.1.typedDimensionDefinitionInExtensionTaxonomy',
|
|
1180
1285
|
modelObject=typedDims,
|
|
1181
1286
|
msg=_('Typed dimensions are not allowed in the extension taxonomy. Update to remove the typed dimension.'))
|
|
1182
1287
|
|
|
1183
1288
|
|
|
1289
|
+
@validation(
|
|
1290
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
1291
|
+
disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
|
|
1292
|
+
)
|
|
1293
|
+
def rule_nl_kvk_4_3_1_1(
|
|
1294
|
+
pluginData: PluginValidationDataExtension,
|
|
1295
|
+
val: ValidateXbrl,
|
|
1296
|
+
*args: Any,
|
|
1297
|
+
**kwargs: Any,
|
|
1298
|
+
) -> Iterable[Validation]:
|
|
1299
|
+
"""
|
|
1300
|
+
NL-KVK.4.3.1.1: Anchoring relationships for elements other than concepts MUST not
|
|
1301
|
+
use 'http://www.esma.europa.eu/xbrl/esef/arcrole/wider-narrower' arcrole
|
|
1302
|
+
"""
|
|
1303
|
+
anchorData = pluginData.getAnchorData(val.modelXbrl)
|
|
1304
|
+
if len(anchorData.extLineItemsWronglyAnchored) > 0:
|
|
1305
|
+
yield Validation.error(
|
|
1306
|
+
codes='NL.NL-KVK.4.3.1.1.unexpectedAnchoringRelationshipsDefinedUsingWiderNarrowerArcrole',
|
|
1307
|
+
modelObject=anchorData.extLineItemsWronglyAnchored,
|
|
1308
|
+
msg=_('A custom element that is not a line item concept is using the wider-narrower arcrole. '
|
|
1309
|
+
'Only line item concepts should use this arcrole. '
|
|
1310
|
+
'Update the extension to no longer include this arcole.')
|
|
1311
|
+
)
|
|
1312
|
+
for anchor in anchorData.anchorsWithDomainItem:
|
|
1313
|
+
yield Validation.error(
|
|
1314
|
+
codes="NL.NL-KVK.4.3.1.1.unexpectedAnchoringRelationshipsDefinedUsingWiderNarrowerArcrole",
|
|
1315
|
+
msg=_("Anchoring relationships MUST be from and to concepts, from %(qname1)s to %(qname2)s"),
|
|
1316
|
+
modelObject=(anchor, anchor.fromModelObject, anchor.toModelObject),
|
|
1317
|
+
qname1=anchor.fromModelObject.qname,
|
|
1318
|
+
qname2=anchor.toModelObject.qname
|
|
1319
|
+
)
|
|
1320
|
+
for anchor in anchorData.anchorsWithDimensionItem:
|
|
1321
|
+
yield Validation.error(
|
|
1322
|
+
codes="NL.NL-KVK.4.3.1.1.unexpectedAnchoringRelationshipsDefinedUsingWiderNarrowerArcrole",
|
|
1323
|
+
msg=_("Anchoring relationships MUST be from and to concepts, from %(qname1)s to %(qname2)s"),
|
|
1324
|
+
modelObject=(anchor, anchor.fromModelObject, anchor.toModelObject),
|
|
1325
|
+
qname1=anchor.fromModelObject.qname,
|
|
1326
|
+
qname2=anchor.toModelObject.qname
|
|
1327
|
+
)
|
|
1328
|
+
|
|
1329
|
+
|
|
1330
|
+
@validation(
|
|
1331
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
1332
|
+
disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
|
|
1333
|
+
)
|
|
1334
|
+
def rule_nl_kvk_4_3_2_1(
|
|
1335
|
+
pluginData: PluginValidationDataExtension,
|
|
1336
|
+
val: ValidateXbrl,
|
|
1337
|
+
*args: Any,
|
|
1338
|
+
**kwargs: Any,
|
|
1339
|
+
) -> Iterable[Validation]:
|
|
1340
|
+
"""
|
|
1341
|
+
NL-KVK.4.3.2.1: Anchoring relationships for concepts MUST be defined in a dedicated
|
|
1342
|
+
extended link role (or roles if needed to properly represent the relationships),
|
|
1343
|
+
e.g. http://{default pattern for roles}/Anchoring.
|
|
1344
|
+
"""
|
|
1345
|
+
anchorData = pluginData.getAnchorData(val.modelXbrl)
|
|
1346
|
+
for elr, rels in anchorData.anchorsInDimensionalElrs.items():
|
|
1347
|
+
yield Validation.error(
|
|
1348
|
+
codes="NL.NL-KVK.4.3.2.1.anchoringRelationshipsForConceptsDefinedInElrContainingDimensionalRelationships",
|
|
1349
|
+
msg=_("Anchoring relationships for concepts MUST be defined in a dedicated extended link role "
|
|
1350
|
+
"(or roles if needed to properly represent the relationships), e.g. "
|
|
1351
|
+
"http://{issuer default pattern for roles}/Anchoring. %(anchoringDimensionalELR)s"),
|
|
1352
|
+
modelObject=rels,
|
|
1353
|
+
anchoringDimensionalELR=elr
|
|
1354
|
+
)
|
|
1355
|
+
|
|
1356
|
+
|
|
1184
1357
|
@validation(
|
|
1185
1358
|
hook=ValidationHook.XBRL_FINALLY,
|
|
1186
1359
|
disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
|
|
@@ -1248,7 +1421,7 @@ def rule_nl_kvk_4_4_2_2(
|
|
|
1248
1421
|
) -> Iterable[Validation]:
|
|
1249
1422
|
"""
|
|
1250
1423
|
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
|
|
1424
|
+
http://xbrl.org/int/dim/arcrole/all arcrole MUST have xbrldt:closed attribute set to "true".
|
|
1252
1425
|
"""
|
|
1253
1426
|
errors = []
|
|
1254
1427
|
extensionData = pluginData.getExtensionData(val.modelXbrl)
|
|
@@ -1276,7 +1449,7 @@ def rule_nl_kvk_4_4_2_3(
|
|
|
1276
1449
|
) -> Iterable[Validation]:
|
|
1277
1450
|
"""
|
|
1278
1451
|
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
|
|
1452
|
+
http://xbrl.org/int/dim/arcrole/notAll arcrole MUST have xbrldt:closed attribute set to "false".
|
|
1280
1453
|
"""
|
|
1281
1454
|
errors = []
|
|
1282
1455
|
extensionData = pluginData.getExtensionData(val.modelXbrl)
|
|
@@ -1292,6 +1465,40 @@ def rule_nl_kvk_4_4_2_3(
|
|
|
1292
1465
|
)
|
|
1293
1466
|
|
|
1294
1467
|
|
|
1468
|
+
@validation(
|
|
1469
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
1470
|
+
disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
|
|
1471
|
+
)
|
|
1472
|
+
def rule_nl_kvk_4_4_2_4(
|
|
1473
|
+
pluginData: PluginValidationDataExtension,
|
|
1474
|
+
val: ValidateXbrl,
|
|
1475
|
+
*args: Any,
|
|
1476
|
+
**kwargs: Any,
|
|
1477
|
+
) -> Iterable[Validation]:
|
|
1478
|
+
"""
|
|
1479
|
+
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
|
|
1480
|
+
extended link role
|
|
1481
|
+
"""
|
|
1482
|
+
elrPrimaryItems = pluginData.getDimensionalData(val.modelXbrl).elrPrimaryItems
|
|
1483
|
+
errors = set()
|
|
1484
|
+
for concept in val.modelXbrl.qnameConcepts.values():
|
|
1485
|
+
if concept.qname not in val.modelXbrl.factsByQname:
|
|
1486
|
+
continue
|
|
1487
|
+
if any(
|
|
1488
|
+
concept in elrPrimaryItems.get(lr, set())
|
|
1489
|
+
for lr in NON_DIMENSIONALIZED_LINE_ITEM_LINKROLES
|
|
1490
|
+
):
|
|
1491
|
+
continue
|
|
1492
|
+
if concept in elrPrimaryItems.get("*", set()):
|
|
1493
|
+
continue
|
|
1494
|
+
errors.add(concept)
|
|
1495
|
+
for error in errors:
|
|
1496
|
+
yield Validation.error(
|
|
1497
|
+
codes='NL.NL-KVK.4.4.2.4.extensionTaxonomyLineItemNotLinkedToAnyHypercube',
|
|
1498
|
+
modelObject=error,
|
|
1499
|
+
msg=_('A non-dimensional concept was not associated to a hypercube. Update relationship so concept is linked to a hypercube.'),
|
|
1500
|
+
)
|
|
1501
|
+
|
|
1295
1502
|
@validation(
|
|
1296
1503
|
hook=ValidationHook.XBRL_FINALLY,
|
|
1297
1504
|
disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
|
|
@@ -1363,6 +1570,156 @@ def rule_nl_kvk_4_4_3_2(
|
|
|
1363
1570
|
)
|
|
1364
1571
|
|
|
1365
1572
|
|
|
1573
|
+
@validation(
|
|
1574
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
1575
|
+
disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
|
|
1576
|
+
)
|
|
1577
|
+
def rule_nl_kvk_4_4_4_1(
|
|
1578
|
+
pluginData: PluginValidationDataExtension,
|
|
1579
|
+
val: ValidateXbrl,
|
|
1580
|
+
*args: Any,
|
|
1581
|
+
**kwargs: Any,
|
|
1582
|
+
) -> Iterable[Validation]:
|
|
1583
|
+
"""
|
|
1584
|
+
NL-KVK.4.4.5.1: Custom labels roles SHOULD NOT be used.
|
|
1585
|
+
"""
|
|
1586
|
+
warnings = set()
|
|
1587
|
+
for ELR in val.modelXbrl.relationshipSet(XbrlConst.parentChild).linkRoleUris:
|
|
1588
|
+
relSet = val.modelXbrl.relationshipSet(XbrlConst.parentChild, ELR)
|
|
1589
|
+
for rootConcept in relSet.rootConcepts:
|
|
1590
|
+
warnings = pluginData.checkLabels(set(), val.modelXbrl , rootConcept, relSet, None, set())
|
|
1591
|
+
if len(warnings) > 0:
|
|
1592
|
+
yield Validation.warning(
|
|
1593
|
+
codes='NL.NL-KVK.4.4.4.1.missingPreferredLabelRole',
|
|
1594
|
+
modelObject=warnings,
|
|
1595
|
+
msg=_('Multiple concepts exist in the presentation with the same label role. '
|
|
1596
|
+
'Review presentation if duplicate concepts should exist or separate preferred label roles should be set.'),
|
|
1597
|
+
)
|
|
1598
|
+
|
|
1599
|
+
|
|
1600
|
+
@validation(
|
|
1601
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
1602
|
+
disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
|
|
1603
|
+
)
|
|
1604
|
+
def rule_nl_kvk_4_4_5_1(
|
|
1605
|
+
pluginData: PluginValidationDataExtension,
|
|
1606
|
+
val: ValidateXbrl,
|
|
1607
|
+
*args: Any,
|
|
1608
|
+
**kwargs: Any,
|
|
1609
|
+
) -> Iterable[Validation]:
|
|
1610
|
+
"""
|
|
1611
|
+
NL-KVK.4.4.5.1: Custom labels roles SHOULD NOT be used.
|
|
1612
|
+
"""
|
|
1613
|
+
warnings = []
|
|
1614
|
+
labelsRelationshipSet = val.modelXbrl.relationshipSet(XbrlConst.conceptLabel)
|
|
1615
|
+
if not labelsRelationshipSet:
|
|
1616
|
+
return
|
|
1617
|
+
for labelRels in labelsRelationshipSet.fromModelObjects().values():
|
|
1618
|
+
for labelRel in labelRels:
|
|
1619
|
+
label = cast(ModelResource, labelRel.toModelObject)
|
|
1620
|
+
if label.role in XbrlConst.standardLabelRoles:
|
|
1621
|
+
continue
|
|
1622
|
+
roleType = val.modelXbrl.roleTypes.get(label.role)
|
|
1623
|
+
if roleType is not None and \
|
|
1624
|
+
roleType[0].modelDocument.uri.startswith("http://www.xbrl.org/lrr"):
|
|
1625
|
+
continue
|
|
1626
|
+
warnings.append(label)
|
|
1627
|
+
if len(warnings) > 0:
|
|
1628
|
+
yield Validation.warning(
|
|
1629
|
+
codes='NL.NL-KVK.4.4.5.1.taxonomyElementLabelCustomRole',
|
|
1630
|
+
modelObject=warnings,
|
|
1631
|
+
msg=_('A custom label role has been used. Update to label role to non-custom.'),
|
|
1632
|
+
)
|
|
1633
|
+
|
|
1634
|
+
|
|
1635
|
+
@validation(
|
|
1636
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
1637
|
+
disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
|
|
1638
|
+
)
|
|
1639
|
+
def rule_nl_kvk_4_4_5_2(
|
|
1640
|
+
pluginData: PluginValidationDataExtension,
|
|
1641
|
+
val: ValidateXbrl,
|
|
1642
|
+
*args: Any,
|
|
1643
|
+
**kwargs: Any,
|
|
1644
|
+
) -> Iterable[Validation]:
|
|
1645
|
+
"""
|
|
1646
|
+
NL-KVK.4.4.5.2: Extension taxonomy elements SHOULD be assigned with at most one label for any combination of role and language.
|
|
1647
|
+
Additionally, extension taxonomies shall not override or replace standard labels of elements referenced in the KVK taxonomy.
|
|
1648
|
+
"""
|
|
1649
|
+
labelsRelationshipSet = val.modelXbrl.relationshipSet(XbrlConst.conceptLabel)
|
|
1650
|
+
extensionData = pluginData.getExtensionData(val.modelXbrl)
|
|
1651
|
+
extensionConcepts = extensionData.extensionConcepts
|
|
1652
|
+
for concept in val.modelXbrl.qnameConcepts.values():
|
|
1653
|
+
conceptLangRoleLabels = defaultdict(list)
|
|
1654
|
+
labelRels = labelsRelationshipSet.fromModelObject(concept)
|
|
1655
|
+
for labelRel in labelRels:
|
|
1656
|
+
label = cast(ModelResource, labelRel.toModelObject)
|
|
1657
|
+
conceptLangRoleLabels[(label.xmlLang, label.role)].append(labelRel.toModelObject)
|
|
1658
|
+
for (lang, labelRole), labels in conceptLangRoleLabels.items():
|
|
1659
|
+
if concept in extensionConcepts and len(labels) > 1:
|
|
1660
|
+
yield Validation.error(
|
|
1661
|
+
codes='NL.NL-KVK.4.4.5.2.taxonomyElementDuplicateLabels',
|
|
1662
|
+
msg=_('A concept was found with more than one label role for related language. '
|
|
1663
|
+
'Update to only one combination. Language: %(lang)s, Role: %(labelRole)s, Concept: %(concept)s.'),
|
|
1664
|
+
modelObject=[concept]+labels, concept=concept.qname, lang=lang, labelRole=labelRole,
|
|
1665
|
+
)
|
|
1666
|
+
elif labelRole == XbrlConst.standardLabel:
|
|
1667
|
+
hasCoreLabel = False
|
|
1668
|
+
hasExtensionLabel = False
|
|
1669
|
+
for label in labels:
|
|
1670
|
+
if pluginData.isExtensionUri(label.modelDocument.uri, val.modelXbrl):
|
|
1671
|
+
hasExtensionLabel = True
|
|
1672
|
+
else:
|
|
1673
|
+
hasCoreLabel = True
|
|
1674
|
+
if hasCoreLabel and hasExtensionLabel:
|
|
1675
|
+
labels_files = ['"%s": %s' % (l.text, l.modelDocument.basename) for l in labels]
|
|
1676
|
+
yield Validation.error(
|
|
1677
|
+
codes='NL.NL-KVK.4.4.5.2.taxonomyElementDuplicateLabels',
|
|
1678
|
+
msg=_("An extension taxonomy defines a standard label for a concept "
|
|
1679
|
+
"already labeled by the base taxonomy. Language: %(lang)s, "
|
|
1680
|
+
"Role: %(labelRole)s, Concept: %(concept)s, Labels: %(labels)s"),
|
|
1681
|
+
modelObject=[concept]+labels, concept=concept.qname, lang=lang,
|
|
1682
|
+
labelRole=labelRole, labels=", ".join(labels_files),
|
|
1683
|
+
)
|
|
1684
|
+
|
|
1685
|
+
|
|
1686
|
+
@validation(
|
|
1687
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
1688
|
+
disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
|
|
1689
|
+
)
|
|
1690
|
+
def rule_nl_kvk_4_4_6_1(
|
|
1691
|
+
pluginData: PluginValidationDataExtension,
|
|
1692
|
+
val: ValidateXbrl,
|
|
1693
|
+
*args: Any,
|
|
1694
|
+
**kwargs: Any,
|
|
1695
|
+
) -> Iterable[Validation]:
|
|
1696
|
+
"""
|
|
1697
|
+
NL-KVK.4.4.6.1: All usable concepts in extension taxonomy relationships SHOULD be applied by tagged facts.
|
|
1698
|
+
"""
|
|
1699
|
+
conceptsUsed = {f.concept for f in val.modelXbrl.facts}
|
|
1700
|
+
unreportedLbLocs = set()
|
|
1701
|
+
for arcrole in (XbrlConst.parentChild, XbrlConst.summationItems, XbrlConst.all, XbrlConst.dimensionDomain, XbrlConst.domainMember):
|
|
1702
|
+
for rel in val.modelXbrl.relationshipSet(arcrole).modelRelationships:
|
|
1703
|
+
for object in (rel.fromModelObject, rel.toModelObject):
|
|
1704
|
+
if (object is None or
|
|
1705
|
+
object.isAbstract or
|
|
1706
|
+
object in conceptsUsed or
|
|
1707
|
+
not pluginData.isExtensionUri(rel.modelDocument.uri, val.modelXbrl)):
|
|
1708
|
+
continue
|
|
1709
|
+
if arcrole in (XbrlConst.parentChild, XbrlConst.summationItems):
|
|
1710
|
+
unreportedLbLocs.add(rel.fromLocator)
|
|
1711
|
+
elif object.type is not None and rel.isUsable and not object.type.isDomainItemType:
|
|
1712
|
+
unreportedLbLocs.add(rel.fromLocator)
|
|
1713
|
+
if len(unreportedLbLocs) > 0:
|
|
1714
|
+
yield Validation.warning(
|
|
1715
|
+
# Subtitle is capitalized inconsistently here because is tmatches the conformance suite. This may change in the future.
|
|
1716
|
+
codes='NL.NL-KVK.4.4.6.1.UsableConceptsNotAppliedByTaggedFacts',
|
|
1717
|
+
modelObject=unreportedLbLocs,
|
|
1718
|
+
msg=_('Axis is missing a default member or the default member does not match the taxonomy defaults. '
|
|
1719
|
+
'Update to set default member based on taxonomy defaults.')
|
|
1720
|
+
)
|
|
1721
|
+
|
|
1722
|
+
|
|
1366
1723
|
@validation(
|
|
1367
1724
|
hook=ValidationHook.XBRL_FINALLY,
|
|
1368
1725
|
disclosureSystems=NL_INLINE_GAAP_OTHER_DISCLOSURE_SYSTEMS,
|
|
@@ -1398,7 +1755,7 @@ def rule_nl_kvk_5_1_3_2(
|
|
|
1398
1755
|
**kwargs: Any,
|
|
1399
1756
|
) -> Iterable[Validation]:
|
|
1400
1757
|
"""
|
|
1401
|
-
NL-KVK.5.1.3.2: The legal entity
|
|
1758
|
+
NL-KVK.5.1.3.2: The legal entity's report MUST import the applicable version of
|
|
1402
1759
|
the taxonomy files prepared by KVK.
|
|
1403
1760
|
"""
|
|
1404
1761
|
reportingPeriod = pluginData.getReportingPeriod(val.modelXbrl)
|
|
@@ -1438,3 +1795,280 @@ def rule_nl_kvk_6_1_1_1(
|
|
|
1438
1795
|
msg=_('The size of the report package must not exceed %(maxSize)s MBs, size is %(size)s MBs.'),
|
|
1439
1796
|
modelObject=val.modelXbrl, maxSize=MAX_REPORT_PACKAGE_SIZE_MBS, size=int(_size/1000000)
|
|
1440
1797
|
)
|
|
1798
|
+
|
|
1799
|
+
|
|
1800
|
+
@validation(
|
|
1801
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
1802
|
+
disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
|
|
1803
|
+
)
|
|
1804
|
+
def rule_nl_kvk_RTS_Annex_IV_Par_11_G4_2_2_1(
|
|
1805
|
+
pluginData: PluginValidationDataExtension,
|
|
1806
|
+
val: ValidateXbrl,
|
|
1807
|
+
*args: Any,
|
|
1808
|
+
**kwargs: Any,
|
|
1809
|
+
) -> Iterable[Validation]:
|
|
1810
|
+
"""
|
|
1811
|
+
NL-KVK.RTS_Annex_IV_Par_11_G4-2-2_1: Extension taxonomy MUST NOT define a custom type if a matching
|
|
1812
|
+
type is defined by the XBRL 2.1 specification or in the XBRL Data Types Registry.
|
|
1813
|
+
Similar to ESEF.RTS.Annex.IV.Par.11.customDataTypeDuplicatingXbrlOrDtrEntry
|
|
1814
|
+
"""
|
|
1815
|
+
errors = []
|
|
1816
|
+
extensionData = pluginData.getExtensionData(val.modelXbrl)
|
|
1817
|
+
for modelDocument, extensionDocumentData in extensionData.extensionDocuments.items():
|
|
1818
|
+
for modelType in modelDocument.xmlRootElement.iterdescendants(tag=XbrlConst.qnXsdComplexType.clarkNotation):
|
|
1819
|
+
if isinstance(modelType, ModelType) and \
|
|
1820
|
+
modelType.typeDerivedFrom is not None and \
|
|
1821
|
+
modelType.typeDerivedFrom.qname.namespaceURI == XbrlConst.xbrli and \
|
|
1822
|
+
not modelType.particlesList:
|
|
1823
|
+
errors.append(modelType)
|
|
1824
|
+
if len(errors) > 0:
|
|
1825
|
+
yield Validation.error(
|
|
1826
|
+
codes='NL.NL-KVK.RTS_Annex_IV_Par_11_G4-2-2_1.customTypeAlreadyDefinedByXbrl',
|
|
1827
|
+
msg=_('A custom data type is being used that matches a standard data type from the XBRL Data Type Registry. '
|
|
1828
|
+
'Update to remove duplicate data types and leverage the standard where appropriate.'),
|
|
1829
|
+
modelObject=errors
|
|
1830
|
+
)
|
|
1831
|
+
|
|
1832
|
+
|
|
1833
|
+
@validation(
|
|
1834
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
1835
|
+
disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
|
|
1836
|
+
)
|
|
1837
|
+
def rule_nl_kvk_RTS_Annex_IV_Par_4_1(
|
|
1838
|
+
pluginData: PluginValidationDataExtension,
|
|
1839
|
+
val: ValidateXbrl,
|
|
1840
|
+
*args: Any,
|
|
1841
|
+
**kwargs: Any,
|
|
1842
|
+
) -> Iterable[Validation]:
|
|
1843
|
+
"""
|
|
1844
|
+
NL-KVK.RTS_Annex_IV_Par_4_1: Extension elements must not duplicate the existing
|
|
1845
|
+
elements from the core taxonomy and be identifiable.
|
|
1846
|
+
"""
|
|
1847
|
+
for name, concepts in val.modelXbrl.nameConcepts.items():
|
|
1848
|
+
if len(concepts) < 2:
|
|
1849
|
+
continue
|
|
1850
|
+
coreConcepts = []
|
|
1851
|
+
extensionConcepts = []
|
|
1852
|
+
for concept in concepts:
|
|
1853
|
+
if pluginData.isExtensionUri(concept.modelDocument.uri, val.modelXbrl):
|
|
1854
|
+
extensionConcepts.append(concept)
|
|
1855
|
+
else:
|
|
1856
|
+
coreConcepts.append(concept)
|
|
1857
|
+
if len(coreConcepts) == 0:
|
|
1858
|
+
continue
|
|
1859
|
+
coreConcept = coreConcepts[0]
|
|
1860
|
+
for extensionConcept in extensionConcepts:
|
|
1861
|
+
if extensionConcept.balance != coreConcept.balance:
|
|
1862
|
+
continue
|
|
1863
|
+
if extensionConcept.periodType != coreConcept.periodType:
|
|
1864
|
+
continue
|
|
1865
|
+
yield Validation.error(
|
|
1866
|
+
codes='NL.NL-KVK.RTS_Annex_IV_Par_4_1.extensionElementDuplicatesCoreElement',
|
|
1867
|
+
msg=_('An extension element was found that is a duplicate to a core element (%(qname)s). '
|
|
1868
|
+
'Review use of element and update to core or revise extension element.'),
|
|
1869
|
+
modelObject=(coreConcept, extensionConcept),
|
|
1870
|
+
qname=coreConcept.qname,
|
|
1871
|
+
)
|
|
1872
|
+
|
|
1873
|
+
|
|
1874
|
+
@validation(
|
|
1875
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
1876
|
+
disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
|
|
1877
|
+
)
|
|
1878
|
+
def rule_nl_kvk_RTS_Annex_IV_Par_4_2(
|
|
1879
|
+
pluginData: PluginValidationDataExtension,
|
|
1880
|
+
val: ValidateXbrl,
|
|
1881
|
+
*args: Any,
|
|
1882
|
+
**kwargs: Any,
|
|
1883
|
+
) -> Iterable[Validation]:
|
|
1884
|
+
"""
|
|
1885
|
+
NL-KVK.RTS_Annex_IV_Par_4_2: Extension elements must be equipped with an appropriate balance attribute.
|
|
1886
|
+
"""
|
|
1887
|
+
errors = []
|
|
1888
|
+
for concept in pluginData.getExtensionConcepts(val.modelXbrl):
|
|
1889
|
+
if concept.isMonetary and concept.balance is None:
|
|
1890
|
+
errors.append(concept)
|
|
1891
|
+
if len(errors) > 0:
|
|
1892
|
+
yield Validation.error(
|
|
1893
|
+
codes='NL.NL-KVK.RTS_Annex_IV_Par_4_2.monetaryConceptWithoutBalance',
|
|
1894
|
+
msg=_('Extension elements must have an appropriate balance attribute.'),
|
|
1895
|
+
modelObject=errors
|
|
1896
|
+
)
|
|
1897
|
+
|
|
1898
|
+
|
|
1899
|
+
@validation(
|
|
1900
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
1901
|
+
disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
|
|
1902
|
+
)
|
|
1903
|
+
def rule_nl_kvk_RTS_Annex_IV_Par_5(
|
|
1904
|
+
pluginData: PluginValidationDataExtension,
|
|
1905
|
+
val: ValidateXbrl,
|
|
1906
|
+
*args: Any,
|
|
1907
|
+
**kwargs: Any,
|
|
1908
|
+
) -> Iterable[Validation]:
|
|
1909
|
+
"""
|
|
1910
|
+
NL-KVK.RTS_Annex_IV_Par_5: Each extension taxonomy element used in tagging
|
|
1911
|
+
must be included in at least one presentation and definition linkbase hierarchy.
|
|
1912
|
+
"""
|
|
1913
|
+
extensionData = pluginData.getExtensionData(val.modelXbrl)
|
|
1914
|
+
taggedExtensionConcepts = set(extensionData.extensionConcepts) & set(fact.concept for fact in val.modelXbrl.facts)
|
|
1915
|
+
|
|
1916
|
+
def getConceptsInLinkbase(arcroles: frozenset[str], concepts: set[ModelConcept]) -> None:
|
|
1917
|
+
for fromModelObject, toRels in val.modelXbrl.relationshipSet(tuple(arcroles)).fromModelObjects().items():
|
|
1918
|
+
if isinstance(fromModelObject, ModelConcept):
|
|
1919
|
+
concepts.add(fromModelObject)
|
|
1920
|
+
for toRel in toRels:
|
|
1921
|
+
if isinstance(toRel.toModelObject, ModelConcept):
|
|
1922
|
+
concepts.add(toRel.toModelObject)
|
|
1923
|
+
|
|
1924
|
+
conceptsInDefinition: set[ModelConcept] = set()
|
|
1925
|
+
getConceptsInLinkbase(LinkbaseType.DEFINITION.getArcroles(), conceptsInDefinition)
|
|
1926
|
+
conceptsMissingFromDefinition = taggedExtensionConcepts - conceptsInDefinition
|
|
1927
|
+
if len(conceptsMissingFromDefinition) > 0:
|
|
1928
|
+
yield Validation.error(
|
|
1929
|
+
codes='NL.NL-KVK.RTS_Annex_IV_Par_5.usableConceptsNotIncludedInDefinitionLink',
|
|
1930
|
+
msg=_('Extension elements are missing from definition linkbase. '
|
|
1931
|
+
'Review use of extension elements.'),
|
|
1932
|
+
modelObject=conceptsMissingFromDefinition
|
|
1933
|
+
)
|
|
1934
|
+
|
|
1935
|
+
conceptsInPresentation: set[ModelConcept] = set()
|
|
1936
|
+
getConceptsInLinkbase(LinkbaseType.PRESENTATION.getArcroles(), conceptsInPresentation)
|
|
1937
|
+
conceptsMissingFromPresentation = taggedExtensionConcepts - conceptsInPresentation
|
|
1938
|
+
if len(conceptsMissingFromPresentation) > 0:
|
|
1939
|
+
yield Validation.error(
|
|
1940
|
+
codes='NL.NL-KVK.RTS_Annex_IV_Par_5.usableConceptsNotIncludedInPresentationLink',
|
|
1941
|
+
msg=_('Extension elements are missing from presentation linkbase. '
|
|
1942
|
+
'Review use of extension elements.'),
|
|
1943
|
+
modelObject=conceptsMissingFromPresentation
|
|
1944
|
+
)
|
|
1945
|
+
|
|
1946
|
+
|
|
1947
|
+
@validation(
|
|
1948
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
1949
|
+
disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
|
|
1950
|
+
)
|
|
1951
|
+
def rule_nl_kvk_RTS_Annex_IV_Par_6(
|
|
1952
|
+
pluginData: PluginValidationDataExtension,
|
|
1953
|
+
val: ValidateXbrl,
|
|
1954
|
+
*args: Any,
|
|
1955
|
+
**kwargs: Any,
|
|
1956
|
+
) -> Iterable[Validation]:
|
|
1957
|
+
"""
|
|
1958
|
+
NL-KVK.RTS_Annex_IV_Par_6: Each NL-GAAP or IFRS financial statements structure MUST be equipped with
|
|
1959
|
+
a calculation linkbase
|
|
1960
|
+
"""
|
|
1961
|
+
hasCalcLinkbase = False
|
|
1962
|
+
extensionData = pluginData.getExtensionData(val.modelXbrl)
|
|
1963
|
+
for modelDoc, extensionDoc in extensionData.extensionDocuments.items():
|
|
1964
|
+
for linkbase in extensionDoc.linkbases:
|
|
1965
|
+
if linkbase.linkbaseType == LinkbaseType.CALCULATION:
|
|
1966
|
+
hasCalcLinkbase = True
|
|
1967
|
+
if not hasCalcLinkbase:
|
|
1968
|
+
yield Validation.error(
|
|
1969
|
+
codes='NL.NL-KVK.RTS_Annex_IV_Par_6.extensionTaxonomyWrongFilesStructure',
|
|
1970
|
+
msg=_('The filing package must include a calculation linkbase.'),
|
|
1971
|
+
modelObject=val.modelXbrl.modelDocument
|
|
1972
|
+
)
|
|
1973
|
+
|
|
1974
|
+
|
|
1975
|
+
@validation(
|
|
1976
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
1977
|
+
disclosureSystems=NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
|
|
1978
|
+
)
|
|
1979
|
+
def rule_nl_kvk_RTS_Annex_IV_Par_8_G4_4_5(
|
|
1980
|
+
pluginData: PluginValidationDataExtension,
|
|
1981
|
+
val: ValidateXbrl,
|
|
1982
|
+
*args: Any,
|
|
1983
|
+
**kwargs: Any,
|
|
1984
|
+
) -> Iterable[Validation]:
|
|
1985
|
+
"""
|
|
1986
|
+
NL-KVK.RTS_Annex_IV_Par_8_G4-4-5: Labels and references of the core
|
|
1987
|
+
taxonomy elements in extension taxonomies of issuer shall not be replaced.
|
|
1988
|
+
"""
|
|
1989
|
+
extensionData = pluginData.getExtensionData(val.modelXbrl)
|
|
1990
|
+
for modelDocument, extensionDoc in extensionData.extensionDocuments.items():
|
|
1991
|
+
for linkbase in extensionDoc.linkbases:
|
|
1992
|
+
if linkbase.prohibitingLabelElements and \
|
|
1993
|
+
linkbase.prohibitedBaseConcepts:
|
|
1994
|
+
if linkbase.linkbaseType == LinkbaseType.LABEL:
|
|
1995
|
+
yield Validation.error(
|
|
1996
|
+
codes='NL.NL-KVK.RTS_Annex_IV_Par_8_G4-4-5.coreTaxonomyLabelModification',
|
|
1997
|
+
msg=_('Standard concept has a modified label from what was defined in the taxonomy. '
|
|
1998
|
+
'Labels from the taxonomy should not be modified.'),
|
|
1999
|
+
modelObject=modelDocument
|
|
2000
|
+
)
|
|
2001
|
+
else:
|
|
2002
|
+
# Assumed to be a reference linkbase.
|
|
2003
|
+
# If anything else, we should probably fire an error anyway.
|
|
2004
|
+
yield Validation.error(
|
|
2005
|
+
codes='NL.NL-KVK.RTS_Annex_IV_Par_8_G4-4-5.coreTaxonomyReferenceModification',
|
|
2006
|
+
msg=_('Standard concept has a modified reference from what was defined in the taxonomy. '
|
|
2007
|
+
'References from the taxonomy should not be modified.'),
|
|
2008
|
+
modelObject=modelDocument
|
|
2009
|
+
)
|
|
2010
|
+
|
|
2011
|
+
|
|
2012
|
+
@validation(
|
|
2013
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
2014
|
+
disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
|
|
2015
|
+
)
|
|
2016
|
+
def rule_nl_kvk_RTS_Art_3(
|
|
2017
|
+
pluginData: PluginValidationDataExtension,
|
|
2018
|
+
val: ValidateXbrl,
|
|
2019
|
+
*args: Any,
|
|
2020
|
+
**kwargs: Any,
|
|
2021
|
+
) -> Iterable[Validation]:
|
|
2022
|
+
"""
|
|
2023
|
+
NL-KVK.RTS_Art_3: Legal entities shall file their annual reports in XHTML format
|
|
2024
|
+
"""
|
|
2025
|
+
for modelDocument in val.modelXbrl.urlDocs.values():
|
|
2026
|
+
docinfo = modelDocument.xmlRootElement.getroottree().docinfo
|
|
2027
|
+
docTypeMatch = DOCTYPE_XHTML_PATTERN.match(docinfo.doctype)
|
|
2028
|
+
if not docTypeMatch:
|
|
2029
|
+
continue
|
|
2030
|
+
if not docTypeMatch.group(1) or docTypeMatch.group(1).lower() == "html":
|
|
2031
|
+
yield Validation.error(
|
|
2032
|
+
codes='NL.NL-KVK.RTS_Art_3.htmlDoctype',
|
|
2033
|
+
msg=_('Doctype SHALL NOT specify html: %(doctype)s'),
|
|
2034
|
+
modelObject=val.modelXbrl.modelDocument,
|
|
2035
|
+
doctype=docinfo.doctype,
|
|
2036
|
+
)
|
|
2037
|
+
else:
|
|
2038
|
+
yield Validation.warning(
|
|
2039
|
+
codes='NL.NL-KVK.RTS_Art_3.xhtmlDoctype',
|
|
2040
|
+
msg=_('Doctype implies xhtml DTD validation but '
|
|
2041
|
+
'inline 1.1 requires schema validation: %(doctype)s'),
|
|
2042
|
+
modelObject=val.modelXbrl.modelDocument,
|
|
2043
|
+
doctype=docinfo.doctype,
|
|
2044
|
+
)
|
|
2045
|
+
|
|
2046
|
+
|
|
2047
|
+
@validation(
|
|
2048
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
2049
|
+
disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
|
|
2050
|
+
)
|
|
2051
|
+
def rule_nl_kvk_RTS_Art_6_a(
|
|
2052
|
+
pluginData: PluginValidationDataExtension,
|
|
2053
|
+
val: ValidateXbrl,
|
|
2054
|
+
*args: Any,
|
|
2055
|
+
**kwargs: Any,
|
|
2056
|
+
) -> Iterable[Validation]:
|
|
2057
|
+
"""
|
|
2058
|
+
NL-KVK.RTS_Art_6_a: Legal entities shall embed markups in the annual reports
|
|
2059
|
+
in XHTML format using the Inline XBRL specifications
|
|
2060
|
+
"""
|
|
2061
|
+
for modelDocument in val.modelXbrl.urlDocs.values():
|
|
2062
|
+
if modelDocument.type != ModelDocument.Type.INLINEXBRL:
|
|
2063
|
+
continue
|
|
2064
|
+
factElements = list(modelDocument.xmlRootElement.iterdescendants(
|
|
2065
|
+
modelDocument.ixNStag + "nonNumeric",
|
|
2066
|
+
modelDocument.ixNStag + "nonFraction",
|
|
2067
|
+
modelDocument.ixNStag + "fraction"
|
|
2068
|
+
))
|
|
2069
|
+
if len(factElements) == 0:
|
|
2070
|
+
yield Validation.error(
|
|
2071
|
+
codes='NL.NL-KVK.RTS_Art_6_a.noInlineXbrlTags',
|
|
2072
|
+
msg=_('Annual report is using xhtml extension, but there are no inline mark up tags identified.'),
|
|
2073
|
+
modelObject=modelDocument,
|
|
2074
|
+
)
|