arelle-release 2.37.46__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 (204) hide show
  1. arelle/BetaFeatures.py +0 -21
  2. arelle/Cntlr.py +15 -8
  3. arelle/CntlrCmdLine.py +121 -56
  4. arelle/CntlrWinMain.py +143 -70
  5. arelle/DialogFind.py +1 -1
  6. arelle/DialogPluginManager.py +6 -4
  7. arelle/DisclosureSystem.py +7 -0
  8. arelle/ErrorManager.py +21 -6
  9. arelle/FileSource.py +11 -4
  10. arelle/FunctionIxt.py +16 -11
  11. arelle/HtmlUtil.py +5 -4
  12. arelle/LeiUtil.py +63 -43
  13. arelle/ModelDocument.py +20 -15
  14. arelle/ModelDtsObject.py +8 -0
  15. arelle/ModelInstanceObject.py +1 -1
  16. arelle/ModelObject.py +16 -18
  17. arelle/ModelObjectFactory.py +35 -17
  18. arelle/ModelXbrl.py +28 -11
  19. arelle/PluginManager.py +130 -105
  20. arelle/RuntimeOptions.py +1 -0
  21. arelle/UrlUtil.py +14 -0
  22. arelle/Validate.py +17 -12
  23. arelle/ValidateDuplicateFacts.py +3 -1
  24. arelle/ValidateFileSource.py +38 -0
  25. arelle/ValidateFilingText.py +3 -3
  26. arelle/ValidateXbrl.py +5 -2
  27. arelle/ValidateXbrlCalcs.py +210 -186
  28. arelle/ValidateXbrlDTS.py +1 -1
  29. arelle/ViewFile.py +1 -0
  30. arelle/ViewFileFactTable.py +2 -2
  31. arelle/ViewWinDTS.py +4 -1
  32. arelle/WebCache.py +28 -24
  33. arelle/XbrlConst.py +22 -0
  34. arelle/XmlUtil.py +16 -21
  35. arelle/XmlValidate.py +6 -9
  36. arelle/_version.py +16 -3
  37. arelle/api/Session.py +11 -2
  38. arelle/config/disclosuresystems.xsd +2 -0
  39. arelle/config/rosettaEntitlements.plist +8 -0
  40. arelle/conformance/CSVTestcaseLoader.py +1 -1
  41. arelle/formula/XPathContext.py +3 -3
  42. arelle/logging/formatters/LogFormatter.py +3 -1
  43. arelle/packages/report/ReportPackage.py +26 -13
  44. arelle/packages/report/ReportPackageConst.py +0 -1
  45. arelle/plugin/inlineXbrlDocumentSet.py +19 -5
  46. arelle/plugin/validate/DBA/DisclosureSystems.py +19 -1
  47. arelle/plugin/validate/DBA/PluginValidationDataExtension.py +2 -4
  48. arelle/plugin/validate/DBA/ValidationPluginExtension.py +2 -1
  49. arelle/plugin/validate/DBA/resources/config.xml +5 -0
  50. arelle/plugin/validate/DBA/rules/__init__.py +2 -2
  51. arelle/plugin/validate/DBA/rules/fr.py +19 -2
  52. arelle/plugin/validate/DBA/rules/tc.py +2 -0
  53. arelle/plugin/validate/DBA/rules/th.py +6 -0
  54. arelle/plugin/validate/DBA/rules/tm.py +18 -5
  55. arelle/plugin/validate/DBA/rules/tr.py +11 -5
  56. arelle/plugin/validate/EDINET/Constants.py +193 -9
  57. arelle/plugin/validate/EDINET/ContextRequirement.py +58 -0
  58. arelle/plugin/validate/EDINET/ControllerPluginData.py +220 -1
  59. arelle/plugin/validate/EDINET/CoverItemRequirements.py +42 -0
  60. arelle/plugin/validate/EDINET/DeiRequirements.py +118 -0
  61. arelle/plugin/validate/EDINET/FilingFormat.py +275 -0
  62. arelle/plugin/validate/EDINET/FormType.py +134 -0
  63. arelle/plugin/validate/EDINET/ManifestInstance.py +72 -5
  64. arelle/plugin/validate/EDINET/NamespaceConfig.py +50 -0
  65. arelle/plugin/validate/EDINET/PluginValidationDataExtension.py +493 -132
  66. arelle/plugin/validate/EDINET/{InstanceType.py → ReportFolderType.py} +72 -15
  67. arelle/plugin/validate/EDINET/Statement.py +139 -0
  68. arelle/plugin/validate/EDINET/TableOfContentsBuilder.py +595 -0
  69. arelle/plugin/validate/EDINET/UploadContents.py +48 -0
  70. arelle/plugin/validate/EDINET/ValidationPluginExtension.py +20 -2
  71. arelle/plugin/validate/EDINET/__init__.py +31 -6
  72. arelle/plugin/validate/EDINET/resources/config.xml +8 -1
  73. arelle/plugin/validate/EDINET/resources/cover-item-requirements.json +793 -0
  74. arelle/plugin/validate/EDINET/resources/dei-requirements.csv +27 -0
  75. arelle/plugin/validate/EDINET/resources/edinet-taxonomies.xml +2 -0
  76. arelle/plugin/validate/EDINET/rules/contexts.py +375 -14
  77. arelle/plugin/validate/EDINET/rules/edinet.py +1934 -45
  78. arelle/plugin/validate/EDINET/rules/frta.py +122 -3
  79. arelle/plugin/validate/EDINET/rules/gfm.py +1907 -11
  80. arelle/plugin/validate/EDINET/rules/upload.py +989 -141
  81. arelle/plugin/validate/ESEF/Const.py +3 -1
  82. arelle/plugin/validate/ESEF/ESEF_2021/DTS.py +5 -0
  83. arelle/plugin/validate/ESEF/ESEF_2021/Image.py +2 -2
  84. arelle/plugin/validate/ESEF/ESEF_2021/ValidateXbrlFinally.py +23 -20
  85. arelle/plugin/validate/ESEF/ESEF_Current/DTS.py +47 -14
  86. arelle/plugin/validate/ESEF/ESEF_Current/ValidateXbrlFinally.py +100 -25
  87. arelle/plugin/validate/ESEF/__init__.py +20 -6
  88. arelle/plugin/validate/ESEF/resources/authority-validations.json +76 -9
  89. arelle/plugin/validate/ESEF/resources/config.xml +20 -0
  90. arelle/plugin/validate/NL/DisclosureSystems.py +22 -0
  91. arelle/plugin/validate/NL/PluginValidationDataExtension.py +27 -9
  92. arelle/plugin/validate/NL/ValidationPluginExtension.py +51 -7
  93. arelle/plugin/validate/NL/resources/config.xml +18 -0
  94. arelle/plugin/validate/NL/rules/br_kvk.py +17 -61
  95. arelle/plugin/validate/NL/rules/fg_nl.py +7 -38
  96. arelle/plugin/validate/NL/rules/fr_kvk.py +7 -42
  97. arelle/plugin/validate/NL/rules/fr_nl.py +31 -147
  98. arelle/plugin/validate/NL/rules/nl_kvk.py +142 -28
  99. arelle/plugin/validate/ROS/PluginValidationDataExtension.py +2 -0
  100. arelle/plugin/validate/ROS/ValidationPluginExtension.py +4 -1
  101. arelle/plugin/validate/ROS/rules/ros.py +41 -9
  102. arelle/plugin/validate/UK/ValidateUK.py +130 -66
  103. arelle/plugin/validate/UK/__init__.py +89 -103
  104. arelle/utils/EntryPointDetection.py +79 -13
  105. arelle/utils/PluginHooks.py +125 -0
  106. arelle/utils/validate/ESEFImage.py +6 -6
  107. arelle/utils/validate/Validation.py +18 -0
  108. arelle/utils/validate/ValidationPlugin.py +76 -11
  109. arelle/utils/validate/ValidationUtil.py +35 -3
  110. {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/METADATA +30 -20
  111. {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/RECORD +115 -191
  112. {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/licenses/LICENSE.md +0 -3
  113. arelle/archive/CustomLogger.py +0 -43
  114. arelle/archive/LoadEFMvalidate.py +0 -32
  115. arelle/archive/LoadSavePreLbCsv.py +0 -26
  116. arelle/archive/LoadValidate.cs +0 -31
  117. arelle/archive/LoadValidate.py +0 -36
  118. arelle/archive/LoadValidateCmdLine.java +0 -69
  119. arelle/archive/LoadValidatePostedZip.java +0 -57
  120. arelle/archive/LoadValidateWebService.java +0 -34
  121. arelle/archive/SaveTableToExelle.py +0 -140
  122. arelle/archive/TR3toTR4.py +0 -88
  123. arelle/archive/plugin/ESEF_2022/__init__.py +0 -47
  124. arelle/archive/plugin/bigInstance.py +0 -394
  125. arelle/archive/plugin/cmdWebServerExtension.py +0 -43
  126. arelle/archive/plugin/crashTest.py +0 -38
  127. arelle/archive/plugin/functionsXmlCreation.py +0 -106
  128. arelle/archive/plugin/hello_i18n.pot +0 -26
  129. arelle/archive/plugin/hello_i18n.py +0 -32
  130. arelle/archive/plugin/importTestChild1.py +0 -21
  131. arelle/archive/plugin/importTestChild2.py +0 -22
  132. arelle/archive/plugin/importTestGrandchild1.py +0 -21
  133. arelle/archive/plugin/importTestGrandchild2.py +0 -21
  134. arelle/archive/plugin/importTestImported1.py +0 -23
  135. arelle/archive/plugin/importTestImported11.py +0 -22
  136. arelle/archive/plugin/importTestParent.py +0 -48
  137. arelle/archive/plugin/instanceInfo.py +0 -306
  138. arelle/archive/plugin/loadFromOIM-2018.py +0 -1282
  139. arelle/archive/plugin/locale/fr/LC_MESSAGES/hello_i18n.po +0 -25
  140. arelle/archive/plugin/objectmaker.py +0 -285
  141. arelle/archive/plugin/packagedImportTest/__init__.py +0 -47
  142. arelle/archive/plugin/packagedImportTest/importTestChild1.py +0 -21
  143. arelle/archive/plugin/packagedImportTest/importTestChild2.py +0 -22
  144. arelle/archive/plugin/packagedImportTest/importTestGrandchild1.py +0 -21
  145. arelle/archive/plugin/packagedImportTest/importTestGrandchild2.py +0 -21
  146. arelle/archive/plugin/packagedImportTest/importTestImported1.py +0 -24
  147. arelle/archive/plugin/packagedImportTest/importTestImported11.py +0 -21
  148. arelle/archive/plugin/packagedImportTest/subdir/importTestImported111.py +0 -21
  149. arelle/archive/plugin/packagedImportTest/subdir/subsubdir/importTestImported1111.py +0 -21
  150. arelle/archive/plugin/sakaCalendar.py +0 -215
  151. arelle/archive/plugin/saveInstanceInfoset.py +0 -121
  152. arelle/archive/plugin/sphinx/FormulaGenerator.py +0 -823
  153. arelle/archive/plugin/sphinx/SphinxContext.py +0 -404
  154. arelle/archive/plugin/sphinx/SphinxEvaluator.py +0 -783
  155. arelle/archive/plugin/sphinx/SphinxMethods.py +0 -1287
  156. arelle/archive/plugin/sphinx/SphinxParser.py +0 -1093
  157. arelle/archive/plugin/sphinx/SphinxValidator.py +0 -163
  158. arelle/archive/plugin/sphinx/US-GAAP Ratios Example.xsr +0 -52
  159. arelle/archive/plugin/sphinx/__init__.py +0 -285
  160. arelle/archive/plugin/streamingExtensions.py +0 -335
  161. arelle/archive/plugin/updateTableLB.py +0 -242
  162. arelle/archive/plugin/validate/SBRnl/CustomLoader.py +0 -19
  163. arelle/archive/plugin/validate/SBRnl/DTS.py +0 -305
  164. arelle/archive/plugin/validate/SBRnl/Dimensions.py +0 -357
  165. arelle/archive/plugin/validate/SBRnl/Document.py +0 -799
  166. arelle/archive/plugin/validate/SBRnl/Filing.py +0 -467
  167. arelle/archive/plugin/validate/SBRnl/__init__.py +0 -75
  168. arelle/archive/plugin/validate/SBRnl/config.xml +0 -26
  169. arelle/archive/plugin/validate/SBRnl/sbr-nl-taxonomies.xml +0 -754
  170. arelle/archive/plugin/validate/USBestPractices.py +0 -570
  171. arelle/archive/plugin/validate/USCorpAction.py +0 -557
  172. arelle/archive/plugin/validate/USSecTagging.py +0 -337
  173. arelle/archive/plugin/validate/XDC/__init__.py +0 -77
  174. arelle/archive/plugin/validate/XDC/config.xml +0 -20
  175. arelle/archive/plugin/validate/XFsyntax/__init__.py +0 -64
  176. arelle/archive/plugin/validate/XFsyntax/xf.py +0 -2227
  177. arelle/archive/plugin/validate/calc2.py +0 -536
  178. arelle/archive/plugin/validateSchemaLxml.py +0 -156
  179. arelle/archive/plugin/validateTableInfoset.py +0 -52
  180. arelle/archive/us-gaap-dei-docType-extraction-frm.xml +0 -90
  181. arelle/archive/us-gaap-dei-ratio-cash-frm.xml +0 -150
  182. arelle/examples/plugin/formulaSuiteConverter.py +0 -212
  183. arelle/examples/plugin/functionsCustom.py +0 -59
  184. arelle/examples/plugin/hello_dolly.py +0 -64
  185. arelle/examples/plugin/multi.py +0 -58
  186. arelle/examples/plugin/rssSaveOim.py +0 -96
  187. arelle/examples/plugin/validate/XYZ/DisclosureSystems.py +0 -2
  188. arelle/examples/plugin/validate/XYZ/PluginValidationDataExtension.py +0 -10
  189. arelle/examples/plugin/validate/XYZ/ValidationPluginExtension.py +0 -49
  190. arelle/examples/plugin/validate/XYZ/__init__.py +0 -75
  191. arelle/examples/plugin/validate/XYZ/resources/config.xml +0 -16
  192. arelle/examples/plugin/validate/XYZ/rules/__init__.py +0 -0
  193. arelle/examples/plugin/validate/XYZ/rules/rules01.py +0 -110
  194. arelle/examples/plugin/validate/XYZ/rules/rules02.py +0 -59
  195. arelle/model/CommentBase.py +0 -9
  196. arelle/model/ElementBase.py +0 -11
  197. arelle/model/PIBase.py +0 -10
  198. arelle/model/__init__.py +0 -15
  199. arelle/scripts-macOS/startWebServer.command +0 -3
  200. arelle/scripts-unix/startWebServer.sh +0 -1
  201. arelle/scripts-windows/startWebServer.bat +0 -5
  202. {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/WHEEL +0 -0
  203. {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/entry_points.txt +0 -0
  204. {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/top_level.txt +0 -0
@@ -3,37 +3,41 @@ See COPYRIGHT.md for copyright information.
3
3
  """
4
4
  from __future__ import annotations
5
5
 
6
- import re
7
6
  from collections import defaultdict
8
7
  from collections.abc import Iterable
9
8
  from datetime import date
10
- from typing import Any, cast, TYPE_CHECKING
9
+ from typing import TYPE_CHECKING, Any, cast
11
10
 
11
+ import regex
12
12
  from lxml.etree import Element
13
13
 
14
+ from arelle import ModelDocument, XbrlConst, XmlUtil
14
15
  from arelle.LinkbaseType import LinkbaseType
15
16
  from arelle.ModelDtsObject import ModelConcept, ModelLink, ModelResource, ModelType
16
17
  from arelle.ModelInstanceObject import ModelInlineFact
17
18
  from arelle.ModelObject import ModelObject
18
19
  from arelle.PrototypeDtsObject import PrototypeObject
19
- from arelle.ValidateDuplicateFacts import getDuplicateFactSets
20
- from arelle.XbrlConst import parentChild, standardLabel
21
- from arelle.XmlValidateConst import VALID
22
-
23
- from arelle import XbrlConst, XmlUtil, ModelDocument
24
- from arelle.ValidateXbrl import ValidateXbrl
25
20
  from arelle.typing import TypeGetText
26
21
  from arelle.utils.PluginHooks import ValidationHook
27
22
  from arelle.utils.validate.Decorator import validation
28
23
  from arelle.utils.validate.DetectScriptsInXhtml import containsScriptMarkers
29
24
  from arelle.utils.validate.ESEFImage import ImageValidationParameters, validateImage
30
25
  from arelle.utils.validate.Validation import Validation
31
- from arelle.ValidateDuplicateFacts import getHashEquivalentFactGroups, getAspectEqualFacts
32
- from arelle.utils.validate.ValidationUtil import etreeIterWithDepth
33
- from ..DisclosureSystems import (ALL_NL_INLINE_DISCLOSURE_SYSTEMS, NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
34
- NL_INLINE_GAAP_OTHER_DISCLOSURE_SYSTEMS)
26
+ from arelle.ValidateDuplicateFacts import getAspectEqualFacts, getDuplicateFactSets, getHashEquivalentFactGroups
27
+ from arelle.ValidateXbrl import ValidateXbrl
28
+ from arelle.XbrlConst import parentChild, standardLabel
29
+ from arelle.XmlValidateConst import VALID
30
+
31
+ from ..DisclosureSystems import (
32
+ DISCLOSURE_SYSTEM_NL_INLINE_2025,
33
+ DISCLOSURE_SYSTEM_NL_INLINE_MULTI_TARGET,
34
+ ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
35
+ NL_INLINE_GAAP_IFRS_DISCLOSURE_SYSTEMS,
36
+ NL_INLINE_GAAP_OTHER_DISCLOSURE_SYSTEMS,
37
+ NL_INLINE_MULTI_TARGET_DISCLOSURE_SYSTEMS,
38
+ NL_INLINE_OTHER_DISCLOSURE_SYSTEMS,
39
+ )
35
40
  from ..PluginValidationDataExtension import (
36
- PluginValidationDataExtension,
37
41
  ALLOWABLE_LANGUAGES,
38
42
  DEFAULT_MEMBER_ROLE_URI,
39
43
  DISALLOWED_IXT_NAMESPACES,
@@ -46,15 +50,16 @@ from ..PluginValidationDataExtension import (
46
50
  TAXONOMY_URLS_BY_YEAR,
47
51
  XBRLI_IDENTIFIER_PATTERN,
48
52
  XBRLI_IDENTIFIER_SCHEMA,
53
+ PluginValidationDataExtension,
49
54
  )
50
55
 
51
56
  if TYPE_CHECKING:
52
- from arelle.ModelXbrl import ModelXbrl
53
57
  from arelle.ModelValue import QName
58
+ from arelle.ModelXbrl import ModelXbrl
54
59
 
55
60
  _: TypeGetText
56
61
 
57
- DOCTYPE_XHTML_PATTERN = re.compile(r"^<!(?:DOCTYPE\s+)\s*html(?:PUBLIC\s+)?(?:.*-//W3C//DTD\s+(X?HTML)\s)?.*>$", re.IGNORECASE)
62
+ DOCTYPE_XHTML_PATTERN = regex.compile(r"^<!(?:DOCTYPE\s+)\s*html(?:PUBLIC\s+)?(?:.*-//W3C//DTD\s+(X?HTML)\s)?.*>$", regex.IGNORECASE)
58
63
 
59
64
 
60
65
  def _getReportingPeriodDateValue(modelXbrl: ModelXbrl, qname: QName) -> date | None:
@@ -751,7 +756,7 @@ def rule_nl_kvk_3_5_2_3(
751
756
  """
752
757
  badLangsUsed = set()
753
758
  for ixdsHtmlRootElt in val.modelXbrl.ixdsHtmlElements:
754
- for uncast_elt, depth in etreeIterWithDepth(ixdsHtmlRootElt):
759
+ for uncast_elt in ixdsHtmlRootElt.iter():
755
760
  elt = cast(Any, uncast_elt)
756
761
  xmlLang = elt.get("{http://www.w3.org/XML/1998/namespace}lang")
757
762
  if xmlLang and xmlLang not in ALLOWABLE_LANGUAGES:
@@ -1244,7 +1249,8 @@ def rule_nl_kvk_4_2_2_2(
1244
1249
  **kwargs: Any,
1245
1250
  ) -> Iterable[Validation]:
1246
1251
  """
1247
- 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
1248
1254
  """
1249
1255
  domainMembersWrongType = []
1250
1256
  domainMembers = pluginData.getDimensionalData(val.modelXbrl).domainMembers
@@ -1326,6 +1332,29 @@ def rule_nl_kvk_4_3_1_1(
1326
1332
  )
1327
1333
 
1328
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
+
1329
1358
  @validation(
1330
1359
  hook=ValidationHook.XBRL_FINALLY,
1331
1360
  disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
@@ -1721,23 +1750,27 @@ def rule_nl_kvk_4_4_6_1(
1721
1750
 
1722
1751
  @validation(
1723
1752
  hook=ValidationHook.XBRL_FINALLY,
1724
- disclosureSystems=NL_INLINE_GAAP_OTHER_DISCLOSURE_SYSTEMS,
1753
+ disclosureSystems=NL_INLINE_OTHER_DISCLOSURE_SYSTEMS,
1725
1754
  )
1726
- def rule_nl_kvk_5_1_3_1(
1755
+ def rule_nl_kvk_5_1_3_1_and_6_1_3_1(
1727
1756
  pluginData: PluginValidationDataExtension,
1728
1757
  val: ValidateXbrl,
1729
1758
  *args: Any,
1730
1759
  **kwargs: Any,
1731
1760
  ) -> Iterable[Validation]:
1732
1761
  """
1733
- 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.
1734
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
1735
1765
  """
1736
1766
  uris = {doc[0].uri for doc in val.modelXbrl.namespaceDocs.values()}
1737
1767
  matches = uris & EFFECTIVE_KVK_GAAP_OTHER_ENTRYPOINT_FILES
1738
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'
1739
1772
  yield Validation.error(
1740
- codes='NL.NL-KVK.5.1.3.1.requiredEntryPointOtherGaapNotReferenced',
1773
+ codes=f'NL.NL-KVK.{base_code}',
1741
1774
  msg=_('The extension taxonomy must import the entry point of the taxonomy files prepared by KVK.'),
1742
1775
  modelObject=val.modelXbrl.modelDocument
1743
1776
  )
@@ -1745,24 +1778,27 @@ def rule_nl_kvk_5_1_3_1(
1745
1778
 
1746
1779
  @validation(
1747
1780
  hook=ValidationHook.XBRL_FINALLY,
1748
- disclosureSystems=NL_INLINE_GAAP_OTHER_DISCLOSURE_SYSTEMS,
1781
+ disclosureSystems=NL_INLINE_OTHER_DISCLOSURE_SYSTEMS,
1749
1782
  )
1750
- def rule_nl_kvk_5_1_3_2(
1783
+ def rule_nl_kvk_5_1_3_2_and_6_1_3_2(
1751
1784
  pluginData: PluginValidationDataExtension,
1752
1785
  val: ValidateXbrl,
1753
1786
  *args: Any,
1754
1787
  **kwargs: Any,
1755
1788
  ) -> Iterable[Validation]:
1756
1789
  """
1757
- 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
1758
1791
  the taxonomy files prepared by KVK.
1759
1792
  """
1760
1793
  reportingPeriod = pluginData.getReportingPeriod(val.modelXbrl)
1761
1794
  uris = {doc[0].uri for doc in val.modelXbrl.namespaceDocs.values()}
1762
1795
  matches = uris & TAXONOMY_URLS_BY_YEAR.get(reportingPeriod or '', set())
1763
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'
1764
1800
  yield Validation.error(
1765
- codes='NL.NL-KVK.5.1.3.2.incorrectVersionEntryPointOtherGaapReferenced',
1801
+ codes=f'NL.NL-KVK.{base_code}',
1766
1802
  msg=_('The report MUST import the applicable version of the taxonomy files prepared by KVK '
1767
1803
  'for the reported financial reporting period. Verify the taxonomy version and make sure '
1768
1804
  'that FinancialReportingPeriod are tagged correctly.'),
@@ -1770,25 +1806,103 @@ def rule_nl_kvk_5_1_3_2(
1770
1806
  )
1771
1807
 
1772
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
+
1773
1887
  @validation(
1774
1888
  hook=ValidationHook.XBRL_FINALLY,
1775
1889
  disclosureSystems=ALL_NL_INLINE_DISCLOSURE_SYSTEMS,
1776
1890
  )
1777
- def rule_nl_kvk_6_1_1_1(
1891
+ def rule_nl_kvk_8_1_1_1(
1778
1892
  pluginData: PluginValidationDataExtension,
1779
1893
  val: ValidateXbrl,
1780
1894
  *args: Any,
1781
1895
  **kwargs: Any,
1782
1896
  ) -> Iterable[Validation]:
1783
1897
  """
1784
- 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.
1785
1899
  """
1786
1900
  size = val.modelXbrl.fileSource.getBytesSize()
1787
1901
  if size is None:
1788
1902
  return # File size is not available, cannot validate
1789
1903
  if size > MAX_REPORT_PACKAGE_SIZE_MBS * 1_000_000: # Interpretting MB as megabytes (1,000,000 bytes)
1790
1904
  yield Validation.error(
1791
- codes='NL.NL-KVK.6.1.1.1.reportPackageMaximumSizeExceeded',
1905
+ codes='NL.NL-KVK.8.1.1.1.reportPackageMaximumSizeExceeded',
1792
1906
  msg=_('The size of the report package must not exceed %(maxSize)s MBs, size is %(size)s MBs.'),
1793
1907
  modelObject=val.modelXbrl, maxSize=MAX_REPORT_PACKAGE_SIZE_MBS, size=int(size/1000000)
1794
1908
  )
@@ -35,6 +35,8 @@ SCHEMA_PATTERNS = {
35
35
  "http://www.cro.ie/": re.compile(r"^\d{1,6}$")
36
36
  }
37
37
 
38
+ UK_REF_NS_PATTERN = re.compile(r"^http://xbrl.frc.org.uk/general/\d{4,}-\d{2,}-\d{2,}/ref$")
39
+
38
40
  TR_NAMESPACES = {
39
41
  "http://www.xbrl.org/inlineXBRL/transformation/2010-04-20",
40
42
  "http://www.xbrl.org/inlineXBRL/transformation/2011-07-31",
@@ -2,6 +2,8 @@
2
2
  See COPYRIGHT.md for copyright information.
3
3
  """
4
4
  from __future__ import annotations
5
+
6
+ from arelle.Cntlr import Cntlr
5
7
  from arelle.ValidateXbrl import ValidateXbrl
6
8
  from arelle.typing import TypeGetText
7
9
  from arelle.utils.validate.ValidationPlugin import ValidationPlugin
@@ -10,6 +12,7 @@ from .PluginValidationDataExtension import PluginValidationDataExtension
10
12
 
11
13
  _: TypeGetText
12
14
 
15
+ CURRENCIES_DIMENSION = 'CurrenciesDimension'
13
16
  EQUITY = 'Equity'
14
17
  IE_PROFIT_LOSS = 'ProfitLossBeforeTax'
15
18
  IE_PROFIT_LOSS_ORDINARY = 'ProfitLossOnOrdinaryActivitiesBeforeTax'
@@ -18,7 +21,7 @@ TURNOVER_REVENUE = 'DPLTurnoverRevenue'
18
21
 
19
22
 
20
23
  class ValidationPluginExtension(ValidationPlugin):
21
- def newPluginData(self, validateXbrl: ValidateXbrl) -> PluginValidationDataExtension:
24
+ def newPluginData(self, cntlr: Cntlr, validateXbrl: ValidateXbrl | None) -> PluginValidationDataExtension:
22
25
  return PluginValidationDataExtension(
23
26
  self.name
24
27
  )
@@ -10,6 +10,7 @@ from collections import Counter
10
10
  from collections.abc import Iterable
11
11
  from decimal import Decimal
12
12
 
13
+ from arelle import XbrlConst
13
14
  from arelle.typing import TypeGetText
14
15
  from arelle.ValidateXbrl import ValidateXbrl
15
16
  from collections import defaultdict
@@ -28,8 +29,8 @@ from arelle.utils.validate.Validation import Validation
28
29
  from arelle.ValidateXbrlCalcs import inferredDecimals, rangeValue
29
30
  from arelle.XbrlConst import qnXbrliMonetaryItemType, qnXbrliXbrl, xhtml
30
31
  from arelle.XmlValidateConst import VALID
31
- from ..ValidationPluginExtension import EQUITY, PRINCIPAL_CURRENCY, TURNOVER_REVENUE
32
- from ..PluginValidationDataExtension import MANDATORY_ELEMENTS, SCHEMA_PATTERNS, TR_NAMESPACES, PluginValidationDataExtension
32
+ from ..ValidationPluginExtension import CURRENCIES_DIMENSION, EQUITY, PRINCIPAL_CURRENCY, TURNOVER_REVENUE
33
+ from ..PluginValidationDataExtension import MANDATORY_ELEMENTS, SCHEMA_PATTERNS, TR_NAMESPACES, UK_REF_NS_PATTERN, PluginValidationDataExtension
33
34
 
34
35
 
35
36
  def checkFileEncoding(modelXbrl: ModelXbrl) -> None:
@@ -302,14 +303,14 @@ def rule_ros19(
302
303
  equity_facts = val.modelXbrl.factsByLocalName.get(EQUITY, set())
303
304
  largest_equity_fact = None
304
305
  for e_fact in equity_facts:
305
- if e_fact.xValid >= VALID:
306
+ if e_fact.xValid >= VALID and e_fact.xValue is not None:
306
307
  if largest_equity_fact is None or abs(convertToDecimal(e_fact.xValue)) > abs(convertToDecimal(largest_equity_fact.xValue)):
307
308
  largest_equity_fact = e_fact
308
309
 
309
310
  turnover_facts = val.modelXbrl.factsByLocalName.get(TURNOVER_REVENUE, set())
310
311
  largest_turnover_fact = None
311
312
  for t_fact in turnover_facts:
312
- if t_fact.xValid >= VALID:
313
+ if t_fact.xValid >= VALID and t_fact.xValue is not None:
313
314
  if largest_turnover_fact is None or convertToDecimal(t_fact.xValue) > convertToDecimal(largest_turnover_fact.xValue):
314
315
  largest_turnover_fact = t_fact
315
316
 
@@ -337,27 +338,58 @@ def rule_ros20(
337
338
  used for the majority of monetary facts.
338
339
  """
339
340
  principal_currency_facts = val.modelXbrl.factsByLocalName.get(PRINCIPAL_CURRENCY, set())
340
- principal_currency_values = set(fact.text for fact in principal_currency_facts)
341
+ principal_currency_values = {
342
+ currencyDimensionCode
343
+ for pc_fact in principal_currency_facts
344
+ if (currencyDimensionCode := _getCurrencyDimensionCode(val.modelXbrl, pc_fact))
345
+ }
341
346
  if len(principal_currency_values) != 1:
342
347
  yield Validation.error(
343
348
  "ROS.20",
344
349
  _("'PrincipalCurrencyUsedInBusinessReport' must exist and have a single value. Values found: %(principal_currency_values)s."),
345
350
  modelObject=principal_currency_facts,
346
- principal_currency_values=principal_currency_values,
351
+ principal_currency_values=sorted(principal_currency_values),
347
352
  )
348
353
  else:
349
354
  principal_currency_value = principal_currency_values.pop()
350
355
  monetary_facts = list(val.modelXbrl.factsByDatatype(False, qnXbrliMonetaryItemType))
351
- monetary_units = [list(fact.utrEntries)[0].unitName 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
+ ]
352
359
  unit_counts = Counter(monetary_units)
353
360
  principal_currency_value_count = unit_counts[principal_currency_value]
354
- for unit, count in unit_counts.items():
361
+ for count in unit_counts.values():
355
362
  if count > principal_currency_value_count:
356
363
  yield Validation.warning(
357
364
  "ROS.20",
358
- _("'PrincipalCurrencyUsedInBusinessReport' has a value of %(principal_currency_value)s, "
365
+ _("'PrincipalCurrencyUsedInBusinessReport' has a %(currencies_dimension)s value of %(principal_currency_value)s, "
359
366
  "which must match the functional(majority) unit of the financial statement."),
360
367
  modelObject=principal_currency_facts,
368
+ currencies_dimension=CURRENCIES_DIMENSION,
361
369
  principal_currency_value=principal_currency_value,
362
370
  )
363
371
  break
372
+
373
+ def _getCurrencyDimensionCode(modelXbrl: ModelXbrl, fact: ModelInlineFact) -> str | None:
374
+ if fact.context is None:
375
+ return None
376
+ for dim, mem in fact.context.qnameDims.items():
377
+ if dim.localName != CURRENCIES_DIMENSION:
378
+ continue
379
+ if mem.xValid < VALID:
380
+ return None
381
+ mem_concept = modelXbrl.qnameConcepts.get(mem.xValue)
382
+ if mem_concept is None:
383
+ return None
384
+ for ref_rel in modelXbrl.relationshipSet(XbrlConst.conceptReference).fromModelObject(mem_concept):
385
+ concept_ref = ref_rel.toModelObject
386
+ uk_ref_ns = None
387
+ for ns in concept_ref.nsmap.values():
388
+ if UK_REF_NS_PATTERN.match(ns):
389
+ uk_ref_ns = ns
390
+ break
391
+ if uk_ref_ns is None:
392
+ continue
393
+ if code := concept_ref.findtext(f"{{{uk_ref_ns}}}Code"):
394
+ return code.strip()
395
+ return None