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.
- arelle/BetaFeatures.py +0 -21
- arelle/Cntlr.py +15 -8
- arelle/CntlrCmdLine.py +121 -56
- arelle/CntlrWinMain.py +143 -70
- arelle/DialogFind.py +1 -1
- arelle/DialogPluginManager.py +6 -4
- arelle/DisclosureSystem.py +7 -0
- arelle/ErrorManager.py +21 -6
- arelle/FileSource.py +11 -4
- arelle/FunctionIxt.py +16 -11
- arelle/HtmlUtil.py +5 -4
- arelle/LeiUtil.py +63 -43
- arelle/ModelDocument.py +20 -15
- arelle/ModelDtsObject.py +8 -0
- arelle/ModelInstanceObject.py +1 -1
- arelle/ModelObject.py +16 -18
- arelle/ModelObjectFactory.py +35 -17
- arelle/ModelXbrl.py +28 -11
- arelle/PluginManager.py +130 -105
- arelle/RuntimeOptions.py +1 -0
- arelle/UrlUtil.py +14 -0
- arelle/Validate.py +17 -12
- arelle/ValidateDuplicateFacts.py +3 -1
- arelle/ValidateFileSource.py +38 -0
- arelle/ValidateFilingText.py +3 -3
- arelle/ValidateXbrl.py +5 -2
- arelle/ValidateXbrlCalcs.py +210 -186
- arelle/ValidateXbrlDTS.py +1 -1
- arelle/ViewFile.py +1 -0
- arelle/ViewFileFactTable.py +2 -2
- arelle/ViewWinDTS.py +4 -1
- arelle/WebCache.py +28 -24
- arelle/XbrlConst.py +22 -0
- arelle/XmlUtil.py +16 -21
- arelle/XmlValidate.py +6 -9
- arelle/_version.py +16 -3
- arelle/api/Session.py +11 -2
- arelle/config/disclosuresystems.xsd +2 -0
- arelle/config/rosettaEntitlements.plist +8 -0
- arelle/conformance/CSVTestcaseLoader.py +1 -1
- arelle/formula/XPathContext.py +3 -3
- arelle/logging/formatters/LogFormatter.py +3 -1
- arelle/packages/report/ReportPackage.py +26 -13
- arelle/packages/report/ReportPackageConst.py +0 -1
- arelle/plugin/inlineXbrlDocumentSet.py +19 -5
- arelle/plugin/validate/DBA/DisclosureSystems.py +19 -1
- arelle/plugin/validate/DBA/PluginValidationDataExtension.py +2 -4
- arelle/plugin/validate/DBA/ValidationPluginExtension.py +2 -1
- arelle/plugin/validate/DBA/resources/config.xml +5 -0
- arelle/plugin/validate/DBA/rules/__init__.py +2 -2
- arelle/plugin/validate/DBA/rules/fr.py +19 -2
- arelle/plugin/validate/DBA/rules/tc.py +2 -0
- arelle/plugin/validate/DBA/rules/th.py +6 -0
- arelle/plugin/validate/DBA/rules/tm.py +18 -5
- arelle/plugin/validate/DBA/rules/tr.py +11 -5
- arelle/plugin/validate/EDINET/Constants.py +193 -9
- arelle/plugin/validate/EDINET/ContextRequirement.py +58 -0
- arelle/plugin/validate/EDINET/ControllerPluginData.py +220 -1
- arelle/plugin/validate/EDINET/CoverItemRequirements.py +42 -0
- arelle/plugin/validate/EDINET/DeiRequirements.py +118 -0
- arelle/plugin/validate/EDINET/FilingFormat.py +275 -0
- arelle/plugin/validate/EDINET/FormType.py +134 -0
- arelle/plugin/validate/EDINET/ManifestInstance.py +72 -5
- arelle/plugin/validate/EDINET/NamespaceConfig.py +50 -0
- arelle/plugin/validate/EDINET/PluginValidationDataExtension.py +493 -132
- arelle/plugin/validate/EDINET/{InstanceType.py → ReportFolderType.py} +72 -15
- arelle/plugin/validate/EDINET/Statement.py +139 -0
- arelle/plugin/validate/EDINET/TableOfContentsBuilder.py +595 -0
- arelle/plugin/validate/EDINET/UploadContents.py +48 -0
- arelle/plugin/validate/EDINET/ValidationPluginExtension.py +20 -2
- arelle/plugin/validate/EDINET/__init__.py +31 -6
- arelle/plugin/validate/EDINET/resources/config.xml +8 -1
- arelle/plugin/validate/EDINET/resources/cover-item-requirements.json +793 -0
- arelle/plugin/validate/EDINET/resources/dei-requirements.csv +27 -0
- arelle/plugin/validate/EDINET/resources/edinet-taxonomies.xml +2 -0
- arelle/plugin/validate/EDINET/rules/contexts.py +375 -14
- arelle/plugin/validate/EDINET/rules/edinet.py +1934 -45
- arelle/plugin/validate/EDINET/rules/frta.py +122 -3
- arelle/plugin/validate/EDINET/rules/gfm.py +1907 -11
- arelle/plugin/validate/EDINET/rules/upload.py +989 -141
- arelle/plugin/validate/ESEF/Const.py +3 -1
- arelle/plugin/validate/ESEF/ESEF_2021/DTS.py +5 -0
- arelle/plugin/validate/ESEF/ESEF_2021/Image.py +2 -2
- arelle/plugin/validate/ESEF/ESEF_2021/ValidateXbrlFinally.py +23 -20
- arelle/plugin/validate/ESEF/ESEF_Current/DTS.py +47 -14
- arelle/plugin/validate/ESEF/ESEF_Current/ValidateXbrlFinally.py +100 -25
- arelle/plugin/validate/ESEF/__init__.py +20 -6
- arelle/plugin/validate/ESEF/resources/authority-validations.json +76 -9
- arelle/plugin/validate/ESEF/resources/config.xml +20 -0
- arelle/plugin/validate/NL/DisclosureSystems.py +22 -0
- arelle/plugin/validate/NL/PluginValidationDataExtension.py +27 -9
- arelle/plugin/validate/NL/ValidationPluginExtension.py +51 -7
- arelle/plugin/validate/NL/resources/config.xml +18 -0
- arelle/plugin/validate/NL/rules/br_kvk.py +17 -61
- arelle/plugin/validate/NL/rules/fg_nl.py +7 -38
- arelle/plugin/validate/NL/rules/fr_kvk.py +7 -42
- arelle/plugin/validate/NL/rules/fr_nl.py +31 -147
- arelle/plugin/validate/NL/rules/nl_kvk.py +142 -28
- arelle/plugin/validate/ROS/PluginValidationDataExtension.py +2 -0
- arelle/plugin/validate/ROS/ValidationPluginExtension.py +4 -1
- arelle/plugin/validate/ROS/rules/ros.py +41 -9
- arelle/plugin/validate/UK/ValidateUK.py +130 -66
- arelle/plugin/validate/UK/__init__.py +89 -103
- arelle/utils/EntryPointDetection.py +79 -13
- arelle/utils/PluginHooks.py +125 -0
- arelle/utils/validate/ESEFImage.py +6 -6
- arelle/utils/validate/Validation.py +18 -0
- arelle/utils/validate/ValidationPlugin.py +76 -11
- arelle/utils/validate/ValidationUtil.py +35 -3
- {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/METADATA +30 -20
- {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/RECORD +115 -191
- {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/licenses/LICENSE.md +0 -3
- arelle/archive/CustomLogger.py +0 -43
- arelle/archive/LoadEFMvalidate.py +0 -32
- arelle/archive/LoadSavePreLbCsv.py +0 -26
- arelle/archive/LoadValidate.cs +0 -31
- arelle/archive/LoadValidate.py +0 -36
- arelle/archive/LoadValidateCmdLine.java +0 -69
- arelle/archive/LoadValidatePostedZip.java +0 -57
- arelle/archive/LoadValidateWebService.java +0 -34
- arelle/archive/SaveTableToExelle.py +0 -140
- arelle/archive/TR3toTR4.py +0 -88
- arelle/archive/plugin/ESEF_2022/__init__.py +0 -47
- arelle/archive/plugin/bigInstance.py +0 -394
- arelle/archive/plugin/cmdWebServerExtension.py +0 -43
- arelle/archive/plugin/crashTest.py +0 -38
- arelle/archive/plugin/functionsXmlCreation.py +0 -106
- arelle/archive/plugin/hello_i18n.pot +0 -26
- arelle/archive/plugin/hello_i18n.py +0 -32
- arelle/archive/plugin/importTestChild1.py +0 -21
- arelle/archive/plugin/importTestChild2.py +0 -22
- arelle/archive/plugin/importTestGrandchild1.py +0 -21
- arelle/archive/plugin/importTestGrandchild2.py +0 -21
- arelle/archive/plugin/importTestImported1.py +0 -23
- arelle/archive/plugin/importTestImported11.py +0 -22
- arelle/archive/plugin/importTestParent.py +0 -48
- arelle/archive/plugin/instanceInfo.py +0 -306
- arelle/archive/plugin/loadFromOIM-2018.py +0 -1282
- arelle/archive/plugin/locale/fr/LC_MESSAGES/hello_i18n.po +0 -25
- arelle/archive/plugin/objectmaker.py +0 -285
- arelle/archive/plugin/packagedImportTest/__init__.py +0 -47
- arelle/archive/plugin/packagedImportTest/importTestChild1.py +0 -21
- arelle/archive/plugin/packagedImportTest/importTestChild2.py +0 -22
- arelle/archive/plugin/packagedImportTest/importTestGrandchild1.py +0 -21
- arelle/archive/plugin/packagedImportTest/importTestGrandchild2.py +0 -21
- arelle/archive/plugin/packagedImportTest/importTestImported1.py +0 -24
- arelle/archive/plugin/packagedImportTest/importTestImported11.py +0 -21
- arelle/archive/plugin/packagedImportTest/subdir/importTestImported111.py +0 -21
- arelle/archive/plugin/packagedImportTest/subdir/subsubdir/importTestImported1111.py +0 -21
- arelle/archive/plugin/sakaCalendar.py +0 -215
- arelle/archive/plugin/saveInstanceInfoset.py +0 -121
- arelle/archive/plugin/sphinx/FormulaGenerator.py +0 -823
- arelle/archive/plugin/sphinx/SphinxContext.py +0 -404
- arelle/archive/plugin/sphinx/SphinxEvaluator.py +0 -783
- arelle/archive/plugin/sphinx/SphinxMethods.py +0 -1287
- arelle/archive/plugin/sphinx/SphinxParser.py +0 -1093
- arelle/archive/plugin/sphinx/SphinxValidator.py +0 -163
- arelle/archive/plugin/sphinx/US-GAAP Ratios Example.xsr +0 -52
- arelle/archive/plugin/sphinx/__init__.py +0 -285
- arelle/archive/plugin/streamingExtensions.py +0 -335
- arelle/archive/plugin/updateTableLB.py +0 -242
- arelle/archive/plugin/validate/SBRnl/CustomLoader.py +0 -19
- arelle/archive/plugin/validate/SBRnl/DTS.py +0 -305
- arelle/archive/plugin/validate/SBRnl/Dimensions.py +0 -357
- arelle/archive/plugin/validate/SBRnl/Document.py +0 -799
- arelle/archive/plugin/validate/SBRnl/Filing.py +0 -467
- arelle/archive/plugin/validate/SBRnl/__init__.py +0 -75
- arelle/archive/plugin/validate/SBRnl/config.xml +0 -26
- arelle/archive/plugin/validate/SBRnl/sbr-nl-taxonomies.xml +0 -754
- arelle/archive/plugin/validate/USBestPractices.py +0 -570
- arelle/archive/plugin/validate/USCorpAction.py +0 -557
- arelle/archive/plugin/validate/USSecTagging.py +0 -337
- arelle/archive/plugin/validate/XDC/__init__.py +0 -77
- arelle/archive/plugin/validate/XDC/config.xml +0 -20
- arelle/archive/plugin/validate/XFsyntax/__init__.py +0 -64
- arelle/archive/plugin/validate/XFsyntax/xf.py +0 -2227
- arelle/archive/plugin/validate/calc2.py +0 -536
- arelle/archive/plugin/validateSchemaLxml.py +0 -156
- arelle/archive/plugin/validateTableInfoset.py +0 -52
- arelle/archive/us-gaap-dei-docType-extraction-frm.xml +0 -90
- arelle/archive/us-gaap-dei-ratio-cash-frm.xml +0 -150
- arelle/examples/plugin/formulaSuiteConverter.py +0 -212
- arelle/examples/plugin/functionsCustom.py +0 -59
- arelle/examples/plugin/hello_dolly.py +0 -64
- arelle/examples/plugin/multi.py +0 -58
- arelle/examples/plugin/rssSaveOim.py +0 -96
- arelle/examples/plugin/validate/XYZ/DisclosureSystems.py +0 -2
- arelle/examples/plugin/validate/XYZ/PluginValidationDataExtension.py +0 -10
- arelle/examples/plugin/validate/XYZ/ValidationPluginExtension.py +0 -49
- arelle/examples/plugin/validate/XYZ/__init__.py +0 -75
- arelle/examples/plugin/validate/XYZ/resources/config.xml +0 -16
- arelle/examples/plugin/validate/XYZ/rules/__init__.py +0 -0
- arelle/examples/plugin/validate/XYZ/rules/rules01.py +0 -110
- arelle/examples/plugin/validate/XYZ/rules/rules02.py +0 -59
- arelle/model/CommentBase.py +0 -9
- arelle/model/ElementBase.py +0 -11
- arelle/model/PIBase.py +0 -10
- arelle/model/__init__.py +0 -15
- arelle/scripts-macOS/startWebServer.command +0 -3
- arelle/scripts-unix/startWebServer.sh +0 -1
- arelle/scripts-windows/startWebServer.bat +0 -5
- {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/WHEEL +0 -0
- {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/entry_points.txt +0 -0
- {arelle_release-2.37.46.dist-info → arelle_release-2.38.0.dist-info}/top_level.txt +0 -0
|
@@ -10,7 +10,6 @@ from arelle.FunctionIxt import ixtNamespaces
|
|
|
10
10
|
from arelle.ModelValue import QName, qname
|
|
11
11
|
from arelle.XmlValidate import lexicalPatterns
|
|
12
12
|
|
|
13
|
-
styleIxHiddenPattern = re.compile(r"(.*[^\w]|^)-esef-ix-hidden\s*:\s*([\w.-]+).*")
|
|
14
13
|
styleCssHiddenPattern = re.compile(r"(.*[^\w]|^)display\s*:\s*none([^\w].*|$)")
|
|
15
14
|
datetimePattern = lexicalPatterns["XBRLI_DATEUNION"]
|
|
16
15
|
docTypeXhtmlPattern = re.compile(r"^<!(?:DOCTYPE\s+)\s*html(?:PUBLIC\s+)?(?:.*-//W3C//DTD\s+(X?HTML)\s)?.*>$", re.IGNORECASE)
|
|
@@ -119,6 +118,9 @@ filenameRegexes = {
|
|
|
119
118
|
"ref": r"(.{1,})-[0-9]{4}-[0-9]{2}-[0-9]{2}_ref[.]xml$",
|
|
120
119
|
}
|
|
121
120
|
|
|
121
|
+
reportBasenamePattern = "{base}-{date}-{version}-{lang}"
|
|
122
|
+
reportBasenameRegex = re.compile(r"(.{1,})-[0-9]{4}-[0-9]{2}-[0-9]{2}-[1-9]-[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$")
|
|
123
|
+
|
|
122
124
|
mandatory: set[QName] = set() # mandatory element qnames
|
|
123
125
|
|
|
124
126
|
# hidden references
|
|
@@ -348,6 +348,11 @@ def checkFilingDTS(
|
|
|
348
348
|
val.modelXbrl.error("ESEF.3.1.1.linkbasesNotSeparateFiles",
|
|
349
349
|
_("Each linkbase type MUST be provided in a separate linkbase file, found: %(linkbasesFound)s."),
|
|
350
350
|
modelObject=modelDocument.xmlRootElement, linkbasesFound=", ".join(sorted(linkbasesFound)))
|
|
351
|
+
# As per ESEF conformance test case TC2_invalid, also output an extensionTaxonomyWrongFilesStructure error
|
|
352
|
+
val.modelXbrl.error("ESEF.3.1.1.extensionTaxonomyWrongFilesStructure",
|
|
353
|
+
_("Each linkbase type MUST be provided in a separate linkbase file, please check file %(documentName)s."),
|
|
354
|
+
modelObject=modelDocument.xmlRootElement,
|
|
355
|
+
documentName=modelDocument.basename)
|
|
351
356
|
|
|
352
357
|
# check for any prohibiting dimensionArc's
|
|
353
358
|
for prohibitingArcElt in modelDocument.xmlRootElement.iterdescendants(tag="{http://www.xbrl.org/2003/linkbase}definitionArc"):
|
|
@@ -10,7 +10,7 @@ from lxml.etree import XML, XMLSyntaxError
|
|
|
10
10
|
|
|
11
11
|
from arelle.ModelObject import ModelObject
|
|
12
12
|
from arelle.ModelXbrl import ModelXbrl
|
|
13
|
-
from arelle.UrlUtil import
|
|
13
|
+
from arelle.UrlUtil import isExternalUrl
|
|
14
14
|
from arelle.ValidateFilingText import validateGraphicHeaderType
|
|
15
15
|
from arelle.typing import TypeGetText
|
|
16
16
|
from ..Const import supportedImgTypes
|
|
@@ -89,7 +89,7 @@ def checkSVGContent(
|
|
|
89
89
|
modelXbrl.error(f"{guidance}.executableCodePresent",
|
|
90
90
|
_("Inline XBRL images MUST NOT contain executable code: %(element)s"),
|
|
91
91
|
modelObject=imgElt, element=eltTag)
|
|
92
|
-
elif
|
|
92
|
+
elif isExternalUrl(href):
|
|
93
93
|
modelXbrl.error(f"{guidance}.referencesPointingOutsideOfTheReportingPackagePresent",
|
|
94
94
|
_("Inline XBRL instance document [image] MUST NOT contain any reference pointing to resources outside the reporting package: %(element)s"),
|
|
95
95
|
modelObject=imgElt, element=eltTag)
|
|
@@ -27,7 +27,7 @@ from arelle.PythonUtil import isLegacyAbs, strTruncate
|
|
|
27
27
|
from arelle.utils.Contexts import partitionModelXbrlContexts
|
|
28
28
|
from arelle.utils.Units import partitionModelXbrlUnits
|
|
29
29
|
from arelle.utils.validate.DetectScriptsInXhtml import containsScriptMarkers
|
|
30
|
-
from arelle.UrlUtil import decodeBase64DataImage, isHttpUrl,
|
|
30
|
+
from arelle.UrlUtil import decodeBase64DataImage, isHttpUrl, isExternalUrl
|
|
31
31
|
from arelle.ValidateFilingText import parseImageDataURL
|
|
32
32
|
from arelle.ValidateUtr import ValidateUtr
|
|
33
33
|
from arelle.ValidateXbrl import ValidateXbrl
|
|
@@ -60,7 +60,6 @@ from ..Const import (
|
|
|
60
60
|
esefStatementsOfMonetaryDeclarationNames,
|
|
61
61
|
mandatory,
|
|
62
62
|
styleCssHiddenPattern,
|
|
63
|
-
styleIxHiddenPattern,
|
|
64
63
|
untransformableTypes,
|
|
65
64
|
)
|
|
66
65
|
from ..Dimensions import checkFilingDimensions
|
|
@@ -269,6 +268,9 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
269
268
|
"%(documentSets)s (Document files appear to be in multiple document sets)"),
|
|
270
269
|
modelObject=doc, documentSets=", ".join(sorted(ixdsDocDirs)))
|
|
271
270
|
ixTargetUsage = val.authParam["ixTargetUsage"]
|
|
271
|
+
styleIxHiddenProperty = val.authParam.get("styleIxHiddenProperty")
|
|
272
|
+
if styleIxHiddenProperty:
|
|
273
|
+
styleIxHiddenPattern = re.compile(rf"(.*[^\w]|^){styleIxHiddenProperty}\s*:\s*([\w.-]+).*")
|
|
272
274
|
if modelDocument.type in (ModelDocument.Type.INLINEXBRL, ModelDocument.Type.INLINEXBRLDOCUMENTSET, ModelDocument.Type.UnknownXML):
|
|
273
275
|
hiddenEltIds = {}
|
|
274
276
|
presentedHiddenEltIds = defaultdict(list)
|
|
@@ -307,7 +309,7 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
307
309
|
modelObject=elt, element=eltTag)
|
|
308
310
|
elif eltTag == "img":
|
|
309
311
|
src = elt.get("src","").strip()
|
|
310
|
-
if
|
|
312
|
+
if isExternalUrl(src):
|
|
311
313
|
modelXbrl.error("ESEF.4.1.6.xHTMLDocumentContainsExternalReferences" if val.unconsolidated
|
|
312
314
|
else "ESEF.3.5.1.inlineXbrlDocumentContainsExternalReferences",
|
|
313
315
|
_("Inline XBRL instance documents MUST NOT contain any reference pointing to resources outside the reporting package: %(element)s"),
|
|
@@ -341,7 +343,7 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
341
343
|
_("Images included in the XHTML document SHOULD be base64 encoded: %(src)s."),
|
|
342
344
|
modelObject=elt, src=src[:128])
|
|
343
345
|
if dataURLParts and dataURLParts.mimeSubtype and dataURLParts.data:
|
|
344
|
-
checkImageContents(modelXbrl, elt, dataURLParts.mimeSubtype, False, dataURLParts.data, val.consolidated)
|
|
346
|
+
checkImageContents(modelXbrl, elt, dataURLParts.mimeSubtype, False, dataURLParts.data, val.consolidated) # type: ignore[arg-type]
|
|
345
347
|
else:
|
|
346
348
|
if not dataURLParts.mimeSubtype:
|
|
347
349
|
modelXbrl.error(f"{contentOtherThanXHTMLGuidance}.MIMETypeNotSpecified",
|
|
@@ -353,7 +355,7 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
353
355
|
modelObject=elt, src=src[:128])
|
|
354
356
|
# check for malicious image contents
|
|
355
357
|
try: # allow embedded newlines
|
|
356
|
-
checkImageContents(modelXbrl, elt, dataURLParts.mimeSubtype, False, decodeBase64DataImage(dataURLParts.data), val.consolidated)
|
|
358
|
+
checkImageContents(modelXbrl, elt, dataURLParts.mimeSubtype, False, decodeBase64DataImage(dataURLParts.data), val.consolidated) # type: ignore[arg-type]
|
|
357
359
|
imgContents = None # deref, may be very large
|
|
358
360
|
except binascii.Error as err:
|
|
359
361
|
modelXbrl.error(f"{contentOtherThanXHTMLGuidance}.embeddedImageNotUsingBase64Encoding",
|
|
@@ -365,7 +367,7 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
365
367
|
# or to other sections of the annual financial report.'''
|
|
366
368
|
#elif eltTag == "a":
|
|
367
369
|
# href = elt.get("href","").strip()
|
|
368
|
-
# if
|
|
370
|
+
# if isExternalUrl(href):
|
|
369
371
|
# modelXbrl.error("ESEF.4.1.6.xHTMLDocumentContainsExternalReferences" if val.unconsolidated
|
|
370
372
|
# else "ESEF.3.5.1.inlineXbrlDocumentContainsExternalReferences",
|
|
371
373
|
# _("Inline XBRL instance documents MUST NOT contain any reference pointing to resources outside the reporting package: %(element)s"),
|
|
@@ -420,7 +422,7 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
420
422
|
if isinstance(elt, ModelInlineFootnote):
|
|
421
423
|
checkFootnote(elt, elt.value)
|
|
422
424
|
elif isinstance(elt, ModelResource) and elt.qname == XbrlConst.qnLinkFootnote:
|
|
423
|
-
checkFootnote(elt, elt.value)
|
|
425
|
+
checkFootnote(elt, elt.value) # type: ignore[attr-defined]
|
|
424
426
|
elif isinstance(elt, ModelInlineFact):
|
|
425
427
|
if elt.format is not None and elt.format.namespaceURI not in IXT_NAMESPACES:
|
|
426
428
|
transformRegistryErrors.add(elt)
|
|
@@ -461,17 +463,18 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
461
463
|
modelObject=eligibleForTransformHiddenFacts,
|
|
462
464
|
countEligible=len(eligibleForTransformHiddenFacts),
|
|
463
465
|
elements=", ".join(sorted(set(str(f.qname) for f in eligibleForTransformHiddenFacts))))
|
|
464
|
-
|
|
465
|
-
for
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
466
|
+
if styleIxHiddenProperty:
|
|
467
|
+
for ixdsHtmlRootElt in modelXbrl.ixdsHtmlElements:
|
|
468
|
+
for ixElt in ixdsHtmlRootElt.getroottree().iterfind(".//{http://www.w3.org/1999/xhtml}*[@style]"):
|
|
469
|
+
hiddenFactRefMatch = styleIxHiddenPattern.match(ixElt.get("style",""))
|
|
470
|
+
if hiddenFactRefMatch:
|
|
471
|
+
hiddenFactRef = hiddenFactRefMatch.group(2)
|
|
472
|
+
if hiddenFactRef not in hiddenEltIds:
|
|
473
|
+
modelXbrl.error("ESEF.2.4.1.esefIxHiddenStyleNotLinkingFactInHiddenSection",
|
|
474
|
+
_("\"%(styleIxHiddenProperty)s\" style identifies id attribute of a fact that is not in ix:hidden section: %(factId)s"),
|
|
475
|
+
modelObject=ixElt, styleIxHiddenProperty=styleIxHiddenProperty, factId=hiddenFactRef)
|
|
476
|
+
else:
|
|
477
|
+
presentedHiddenEltIds[hiddenFactRef].append(ixElt)
|
|
475
478
|
for hiddenEltId, ixElt in hiddenEltIds.items():
|
|
476
479
|
if (hiddenEltId not in presentedHiddenEltIds and
|
|
477
480
|
getattr(ixElt, "xValid", 0) >= VALID and # may not be validated
|
|
@@ -529,11 +532,11 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
529
532
|
if contextsWithDisallowedOCEs:
|
|
530
533
|
modelXbrl.error("ESEF.2.1.3.segmentUsed",
|
|
531
534
|
_("xbrli:segment container MUST NOT be used in contexts: %(contextIds)s"),
|
|
532
|
-
modelObject=contextsWithDisallowedOCEs, contextIds=", ".join(c.id for c in contextsWithDisallowedOCEs))
|
|
535
|
+
modelObject=contextsWithDisallowedOCEs, contextIds=", ".join(c.id for c in contextsWithDisallowedOCEs if c.id is not None))
|
|
533
536
|
if contextsWithDisallowedOCEcontent:
|
|
534
537
|
modelXbrl.error("ESEF.2.1.3.scenarioContainsNonDimensionalContent",
|
|
535
538
|
_("xbrli:scenario in contexts MUST NOT contain any other content than defined in XBRL Dimensions specification: %(contextIds)s"),
|
|
536
|
-
modelObject=contextsWithDisallowedOCEcontent, contextIds=", ".join(c.id for c in contextsWithDisallowedOCEcontent))
|
|
539
|
+
modelObject=contextsWithDisallowedOCEcontent, contextIds=", ".join(c.id for c in contextsWithDisallowedOCEcontent if c.id is not None))
|
|
537
540
|
if len(contextIdentifiers) > 1:
|
|
538
541
|
modelXbrl.error("ESEF.2.1.4.multipleIdentifiers",
|
|
539
542
|
_("All entity identifiers in contexts MUST have identical content: %(contextIds)s"),
|
|
@@ -164,21 +164,49 @@ def checkFilingDTS(val: ValidateXbrl, modelDocument: ModelDocument, esefNotesCon
|
|
|
164
164
|
if modelConcept.isMonetary and not modelConcept.balance:
|
|
165
165
|
extMonetaryConceptsWithoutBalance.append(modelConcept)
|
|
166
166
|
if esefDisclosureSystemYear >= 2024:
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
167
|
+
widerRels = widerNarrowerRelSet.fromModelObject(modelConcept)
|
|
168
|
+
narrowerRels = widerNarrowerRelSet.toModelObject(modelConcept)
|
|
169
|
+
|
|
170
|
+
widerTypes = set(
|
|
171
|
+
r.toModelObject.typeQname
|
|
172
|
+
for r in widerRels
|
|
173
|
+
if r.toModelObject is not None
|
|
174
|
+
if r.toModelObject.typeQname is not None
|
|
175
|
+
)
|
|
176
|
+
narrowerTypes = set(
|
|
177
|
+
r.fromModelObject.typeQname
|
|
178
|
+
for r in narrowerRels
|
|
179
|
+
if r.fromModelObject is not None
|
|
180
|
+
if r.fromModelObject.typeQname is not None
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
allAnchorTypes = widerTypes | narrowerTypes
|
|
184
|
+
if esefDisclosureSystemYear == 2024:
|
|
185
|
+
invalidAnchorTypes = any(t != modelConcept.typeQname for t in allAnchorTypes)
|
|
186
|
+
msg = _("Issuers should anchor their extension elements to ESEF core taxonomy "
|
|
187
|
+
"elements sharing the same data type.")
|
|
188
|
+
else:
|
|
189
|
+
invalidAnchorTypes = any(not modelConcept.instanceOfType(t) for t in allAnchorTypes)
|
|
190
|
+
msg = _("Issuers should anchor their extension elements to ESEF core taxonomy "
|
|
191
|
+
"elements with the same data type or a data type derived from the "
|
|
192
|
+
"anchor's data type.")
|
|
193
|
+
if invalidAnchorTypes:
|
|
194
|
+
anchorTypeParts = []
|
|
195
|
+
if widerTypes:
|
|
196
|
+
widerStr = ", ".join(sorted(t.clarkNotation for t in widerTypes))
|
|
197
|
+
anchorTypeParts.append(f"Wider: {widerStr}")
|
|
198
|
+
if narrowerTypes:
|
|
199
|
+
narrowerStr = ", ".join(sorted(t.clarkNotation for t in narrowerTypes))
|
|
200
|
+
anchorTypeParts.append(f"Narrower: {narrowerStr}")
|
|
201
|
+
anchorTypeStr = " ".join(anchorTypeParts)
|
|
202
|
+
val.modelXbrl.warning(
|
|
203
|
+
"ESEF.1.4.1.differentExtensionDataType",
|
|
204
|
+
msg + _(" Concept: %(qname)s type: %(type)s %(anchorTypes)s"),
|
|
205
|
+
modelObject=modelConcept,
|
|
206
|
+
qname=modelConcept.qname,
|
|
207
|
+
type=modelConcept.typeQname.clarkNotation,
|
|
208
|
+
anchorTypes=anchorTypeStr
|
|
178
209
|
)
|
|
179
|
-
val.modelXbrl.warning("ESEF.1.4.1.differentExtensionDataType",
|
|
180
|
-
_("Issuers should anchor their extension elements to ESEF core taxonomy elements sharing the same data type. Concept: %(qname)s type: %(type)s %(widerNarrowerType)s"),
|
|
181
|
-
modelObject=modelConcept, qname=modelConcept.qname, type=modelConcept.typeQname, widerNarrowerType=widerNarrowerType)
|
|
182
210
|
|
|
183
211
|
# check all lang's of standard label
|
|
184
212
|
hasStandardLabel = False
|
|
@@ -386,6 +414,11 @@ def checkFilingDTS(val: ValidateXbrl, modelDocument: ModelDocument, esefNotesCon
|
|
|
386
414
|
val.modelXbrl.error("ESEF.3.1.1.linkbasesNotSeparateFiles",
|
|
387
415
|
_("Each linkbase type MUST be provided in a separate linkbase file, found: %(linkbasesFound)s."),
|
|
388
416
|
modelObject=modelDocument.xmlRootElement, linkbasesFound=", ".join(sorted(linkbasesFound)))
|
|
417
|
+
# As per ESEF conformance test case TC2_invalid, also output an extensionTaxonomyWrongFilesStructure error
|
|
418
|
+
val.modelXbrl.error("ESEF.3.1.1.extensionTaxonomyWrongFilesStructure",
|
|
419
|
+
_("Each linkbase type MUST be provided in a separate linkbase file, please check file %(documentName)s."),
|
|
420
|
+
modelObject=modelDocument.xmlRootElement,
|
|
421
|
+
documentName=modelDocument.basename)
|
|
389
422
|
|
|
390
423
|
# check for any prohibiting dimensionArc's
|
|
391
424
|
for prohibitingArcElt in modelDocument.xmlRootElement.iterdescendants(tag="{http://www.xbrl.org/2003/linkbase}definitionArc"):
|
|
@@ -15,6 +15,7 @@ import tinycss2.ast # type: ignore[import-untyped]
|
|
|
15
15
|
from lxml.etree import _Comment, _Element, _ElementTree, _Entity, _ProcessingInstruction
|
|
16
16
|
|
|
17
17
|
from arelle import LeiUtil, ModelDocument, XbrlConst
|
|
18
|
+
from arelle.FunctionIxt import ixtNamespacesByVersion
|
|
18
19
|
from arelle.ModelDtsObject import ModelConcept
|
|
19
20
|
from arelle.ModelDtsObject import ModelResource
|
|
20
21
|
from arelle.ModelInstanceObject import ModelContext
|
|
@@ -33,7 +34,7 @@ from arelle.PythonUtil import strTruncate
|
|
|
33
34
|
from arelle.utils.Contexts import partitionModelXbrlContexts
|
|
34
35
|
from arelle.utils.Units import partitionModelXbrlUnits
|
|
35
36
|
from arelle.utils.validate.DetectScriptsInXhtml import containsScriptMarkers
|
|
36
|
-
from arelle.UrlUtil import isHttpUrl
|
|
37
|
+
from arelle.UrlUtil import isHttpUrl, isExternalUrl
|
|
37
38
|
from arelle.ValidateUtr import ValidateUtr
|
|
38
39
|
from arelle.ValidateXbrl import ValidateXbrl
|
|
39
40
|
from arelle.ValidateXbrlCalcs import inferredDecimals, rangeValue
|
|
@@ -56,7 +57,6 @@ from .DTS import checkFilingDTS
|
|
|
56
57
|
from ..Const import (
|
|
57
58
|
DefaultDimensionLinkroles,
|
|
58
59
|
FOOTNOTE_LINK_CHILDREN,
|
|
59
|
-
IXT_NAMESPACES,
|
|
60
60
|
LineItemsNotQualifiedLinkroles,
|
|
61
61
|
PERCENT_TYPES, datetimePattern,
|
|
62
62
|
docTypeXhtmlPattern,
|
|
@@ -64,9 +64,10 @@ from ..Const import (
|
|
|
64
64
|
esefPrimaryStatementPlaceholderNames,
|
|
65
65
|
mandatory,
|
|
66
66
|
styleCssHiddenPattern,
|
|
67
|
-
styleIxHiddenPattern,
|
|
68
67
|
supportedImgTypes,
|
|
69
68
|
untransformableTypes,
|
|
69
|
+
reportBasenamePattern,
|
|
70
|
+
reportBasenameRegex
|
|
70
71
|
)
|
|
71
72
|
from ..Dimensions import checkFilingDimensions
|
|
72
73
|
from ..Util import checkForMultiLangDuplicates, getEsefNotesStatementConcepts, isExtension, getDisclosureSystemYear
|
|
@@ -74,6 +75,7 @@ from ..Util import checkForMultiLangDuplicates, getEsefNotesStatementConcepts, i
|
|
|
74
75
|
_: TypeGetText # Handle gettext
|
|
75
76
|
|
|
76
77
|
ifrsNsPattern = re.compile(r"https?://xbrl.ifrs.org/taxonomy/[0-9-]{10}/ifrs-full")
|
|
78
|
+
escapeWorthyStr = re.compile(r".*[<&]")
|
|
77
79
|
|
|
78
80
|
|
|
79
81
|
def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
@@ -294,6 +296,12 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
294
296
|
"http://www.xbrl.org/WGN/report-packages/WGN-2018-08-14/report-packages-WGN-2018-08-14"
|
|
295
297
|
".html: %(fileName)s (Document file not in correct place in package)"),
|
|
296
298
|
modelObject=doc, fileName=doc.basename)
|
|
299
|
+
elif esefDisclosureSystemYear >= 2025:
|
|
300
|
+
m = reportBasenameRegex.match(_baseName)
|
|
301
|
+
if not m:
|
|
302
|
+
modelXbrl.error("ESEF.2.6.3.incorrectNamingConventionReportPackageReportFile",
|
|
303
|
+
_("Inline XBRL document filename SHOULD match %(pattern)s"),
|
|
304
|
+
modelObject=doc, fileName=doc.basename, pattern=reportBasenamePattern)
|
|
297
305
|
else: # non-consolidated
|
|
298
306
|
if docTypeMatch:
|
|
299
307
|
if not docTypeMatch.group(1) or docTypeMatch.group(1).lower() == "html":
|
|
@@ -311,6 +319,17 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
311
319
|
"%(url)s: %(documentSets)s (Document files appear to be in multiple document sets)"),
|
|
312
320
|
modelObject=doc, documentSets=", ".join(sorted(ixdsDocDirs)), url=reportIncorrectlyPlacedInPackageRef)
|
|
313
321
|
ixTargetUsage = val.authParam["ixTargetUsage"]
|
|
322
|
+
earliestTRVersion = val.authParam.get("earliestTransformationRegistryVersion", min(ixtNamespacesByVersion.keys()))
|
|
323
|
+
if earliestTRVersion not in ixtNamespacesByVersion:
|
|
324
|
+
raise ValueError(f'Unsupported "earliestTransformationRegistryVersion" disclosure system config setting: {earliestTRVersion}')
|
|
325
|
+
allowedTRnamespaces = frozenset(
|
|
326
|
+
ns for version, ns in ixtNamespacesByVersion.items()
|
|
327
|
+
if version >= earliestTRVersion
|
|
328
|
+
)
|
|
329
|
+
if styleIxHiddenProperty := val.authParam.get("styleIxHiddenProperty"):
|
|
330
|
+
styleIxHiddenPattern = re.compile(rf"(.*[^\w]|^){styleIxHiddenProperty}\s*:\s*([\w.-]+).*")
|
|
331
|
+
else:
|
|
332
|
+
styleIxHiddenPattern = None
|
|
314
333
|
if modelDocument.type in (ModelDocument.Type.INLINEXBRL, ModelDocument.Type.INLINEXBRLDOCUMENTSET, ModelDocument.Type.UnknownXML):
|
|
315
334
|
hiddenEltIds = {}
|
|
316
335
|
presentedHiddenEltIds = defaultdict(list)
|
|
@@ -355,6 +374,27 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
355
374
|
modelXbrl.error(f"{contentOtherThanXHTMLGuidance}.executableCodePresent",
|
|
356
375
|
_("Inline XBRL documents MUST NOT contain executable code: %(element)s"),
|
|
357
376
|
modelObject=elt, element=eltTag)
|
|
377
|
+
externalReferenceFound = False
|
|
378
|
+
if eltTag == "object":
|
|
379
|
+
# Check if the attribute 'archive' contains an external reference!
|
|
380
|
+
archiveAttr = elt.get("archive", "").strip()
|
|
381
|
+
for possibleURI in archiveAttr.split(" "):
|
|
382
|
+
if isExternalUrl(possibleURI.strip()):
|
|
383
|
+
externalReferenceFound = True
|
|
384
|
+
break
|
|
385
|
+
elif eltTag == "script":
|
|
386
|
+
# Check if the attribute 'src' contains an external reference!
|
|
387
|
+
srcAttr = elt.get("src", "").strip()
|
|
388
|
+
if isExternalUrl(srcAttr):
|
|
389
|
+
externalReferenceFound = True
|
|
390
|
+
if externalReferenceFound:
|
|
391
|
+
modelXbrl.error(
|
|
392
|
+
"ESEF.4.1.6.xHTMLDocumentContainsExternalReferences" if val.unconsolidated
|
|
393
|
+
else "ESEF.3.5.1.inlineXbrlDocumentContainsExternalReferences",
|
|
394
|
+
_("Inline XBRL instance documents MUST NOT contain any reference pointing to resources outside the reporting package: %(element)s"),
|
|
395
|
+
modelObject=elt, element=eltTag,
|
|
396
|
+
messageCodes=("ESEF.3.5.1.inlineXbrlDocumentContainsExternalReferences",
|
|
397
|
+
"ESEF.4.1.6.xHTMLDocumentContainsExternalReferences"))
|
|
358
398
|
elif eltTag == "a" and "mailto" in elt.get("href", ""):
|
|
359
399
|
modelXbrl.warning(f"{contentOtherThanXHTMLGuidance}.executableCodePresent.mailto",
|
|
360
400
|
_("Inline XBRL documents SHOULD NOT contain any 'mailto' URI: %(element)s"),
|
|
@@ -371,7 +411,7 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
371
411
|
# or to other sections of the annual financial report.'''
|
|
372
412
|
#elif eltTag == "a":
|
|
373
413
|
# href = elt.get("href","").strip()
|
|
374
|
-
# if
|
|
414
|
+
# if isExternalUrl(href):
|
|
375
415
|
# modelXbrl.error("ESEF.4.1.6.xHTMLDocumentContainsExternalReferences" if val.unconsolidated
|
|
376
416
|
# else "ESEF.3.5.1.inlineXbrlDocumentContainsExternalReferences",
|
|
377
417
|
# _("Inline XBRL instance documents MUST NOT contain any reference pointing to resources outside the reporting package: %(element)s"),
|
|
@@ -465,9 +505,9 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
465
505
|
if isinstance(elt, ModelInlineFootnote):
|
|
466
506
|
checkFootnote(elt, elt.value)
|
|
467
507
|
elif isinstance(elt, ModelResource) and elt.qname == XbrlConst.qnLinkFootnote:
|
|
468
|
-
checkFootnote(elt, elt.value)
|
|
508
|
+
checkFootnote(elt, elt.value) # type: ignore[attr-defined]
|
|
469
509
|
elif isinstance(elt, ModelInlineFact):
|
|
470
|
-
if elt.format is not None and elt.format.namespaceURI not in
|
|
510
|
+
if elt.format is not None and elt.format.namespaceURI not in allowedTRnamespaces:
|
|
471
511
|
transformRegistryErrors.add(elt)
|
|
472
512
|
ixHiddenFacts = set()
|
|
473
513
|
for ixHiddenElt in ixdsHtmlRootElt.iterdescendants(tag=ixNStag + "hidden"):
|
|
@@ -509,11 +549,15 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
509
549
|
for ixdsHtmlRootElt in modelXbrl.ixdsHtmlElements:
|
|
510
550
|
for ixElt in ixdsHtmlRootElt.getroottree().iterfind(".//{http://www.w3.org/1999/xhtml}*[@style]"):
|
|
511
551
|
styleValue = ixElt.get("style","")
|
|
512
|
-
hiddenFactRefMatch = styleIxHiddenPattern.match(styleValue)
|
|
513
552
|
|
|
514
553
|
if styleValue:
|
|
515
554
|
for declaration in tinycss2.parse_blocks_contents(styleValue):
|
|
516
555
|
if isinstance(declaration, tinycss2.ast.Declaration):
|
|
556
|
+
disallowedHiddenStyle = val.authParam.get("disallowedHiddenStyle")
|
|
557
|
+
if declaration.lower_name == disallowedHiddenStyle:
|
|
558
|
+
modelXbrl.error("ESEF.2.4.1.IxHiddenStyleDisallowed",
|
|
559
|
+
_("Usage of disallowed hidden style: %(styleName)s"),
|
|
560
|
+
modelObject=ixElt, styleName=disallowedHiddenStyle)
|
|
517
561
|
validateCssUrlContent(declaration.value, ixElt.modelDocument.baseForElement(ixElt),
|
|
518
562
|
modelXbrl, val, ixElt, imageValidationParameters)
|
|
519
563
|
elif isinstance(declaration, tinycss2.ast.ParseError):
|
|
@@ -521,14 +565,16 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
521
565
|
_("The style attribute contains erroneous CSS declaration \"%(styleContent)s\": %(parseError)s"),
|
|
522
566
|
modelObject=ixElt, parseError=declaration.message,
|
|
523
567
|
styleContent=styleValue)
|
|
524
|
-
if
|
|
525
|
-
|
|
526
|
-
if
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
568
|
+
if styleIxHiddenPattern is not None:
|
|
569
|
+
hiddenFactRefMatch = styleIxHiddenPattern.match(styleValue)
|
|
570
|
+
if hiddenFactRefMatch:
|
|
571
|
+
hiddenFactRef = hiddenFactRefMatch.group(2)
|
|
572
|
+
if hiddenFactRef not in hiddenEltIds:
|
|
573
|
+
modelXbrl.error("ESEF.2.4.1.esefIxHiddenStyleNotLinkingFactInHiddenSection",
|
|
574
|
+
_("\"%(styleIxHiddenProperty)s\" style identifies id attribute of a fact that is not in ix:hidden section: %(factId)s"),
|
|
575
|
+
modelObject=ixElt, styleIxHiddenProperty=styleIxHiddenProperty, factId=hiddenFactRef)
|
|
576
|
+
else:
|
|
577
|
+
presentedHiddenEltIds[hiddenFactRef].append(ixElt)
|
|
532
578
|
for hiddenEltId, ixElt in hiddenEltIds.items():
|
|
533
579
|
if (hiddenEltId not in presentedHiddenEltIds and
|
|
534
580
|
getattr(ixElt, "xValid", 0) >= VALID and # may not be validated
|
|
@@ -536,9 +582,10 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
536
582
|
requiredToDisplayFacts.append(ixElt)
|
|
537
583
|
if requiredToDisplayFacts:
|
|
538
584
|
modelXbrl.error("ESEF.2.4.1.factInHiddenSectionNotInReport",
|
|
539
|
-
_("The ix:hidden section contains %(countUnreferenced)s fact(s) whose @id is not applied on any \"
|
|
585
|
+
_("The ix:hidden section contains %(countUnreferenced)s fact(s) whose @id is not applied on any \"%(styleIxHiddenProperty)s\" style: %(elements)s"),
|
|
540
586
|
modelObject=requiredToDisplayFacts,
|
|
541
587
|
countUnreferenced=len(requiredToDisplayFacts),
|
|
588
|
+
styleIxHiddenProperty=styleIxHiddenProperty,
|
|
542
589
|
elements=", ".join(sorted(set(str(f.qname) for f in requiredToDisplayFacts))))
|
|
543
590
|
del eligibleForTransformHiddenFacts, hiddenEltIds, presentedHiddenEltIds, requiredToDisplayFacts
|
|
544
591
|
elif modelDocument.type == ModelDocument.Type.INSTANCE:
|
|
@@ -589,11 +636,11 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
589
636
|
if contextsWithDisallowedOCEs:
|
|
590
637
|
modelXbrl.error("ESEF.2.1.3.segmentUsed",
|
|
591
638
|
_("xbrli:segment container MUST NOT be used in contexts: %(contextIds)s"),
|
|
592
|
-
modelObject=contextsWithDisallowedOCEs, contextIds=", ".join(c.id for c in contextsWithDisallowedOCEs))
|
|
639
|
+
modelObject=contextsWithDisallowedOCEs, contextIds=", ".join(c.id for c in contextsWithDisallowedOCEs if c.id is not None))
|
|
593
640
|
if contextsWithDisallowedOCEcontent:
|
|
594
641
|
modelXbrl.error("ESEF.2.1.3.scenarioContainsNonDimensionalContent",
|
|
595
642
|
_("xbrli:scenario in contexts MUST NOT contain any other content than defined in XBRL Dimensions specification: %(contextIds)s"),
|
|
596
|
-
modelObject=contextsWithDisallowedOCEcontent, contextIds=", ".join(c.id for c in contextsWithDisallowedOCEcontent))
|
|
643
|
+
modelObject=contextsWithDisallowedOCEcontent, contextIds=", ".join(c.id for c in contextsWithDisallowedOCEcontent if c.id is not None))
|
|
597
644
|
if len(contextIdentifiers) > 1:
|
|
598
645
|
modelXbrl.error("ESEF.2.1.4.multipleIdentifiers",
|
|
599
646
|
_("All entity identifiers in contexts MUST have identical content: %(contextIds)s"),
|
|
@@ -669,10 +716,22 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
669
716
|
if esefDisclosureSystemYear >= 2024:
|
|
670
717
|
if not f.id:
|
|
671
718
|
factsMissingId.append(f)
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
719
|
+
errorMsg = _("Facts with datatype 'dtr-types:textBlockItemType' MUST use the 'escape' attribute set to 'true'.")
|
|
720
|
+
if isinstance(f, ModelInlineFact) and f.concept is not None and esefDisclosureSystemYear >= 2024:
|
|
721
|
+
if esefDisclosureSystemYear == 2024:
|
|
722
|
+
errorMsg += _(" Facts with any other datatype MUST use the 'escape' attribute set to 'false'")
|
|
723
|
+
hasEscapeIssue = f.isEscaped != f.concept.isTextBlock
|
|
724
|
+
elif esefDisclosureSystemYear >= 2025:
|
|
725
|
+
hasEscapeIssue = not f.isEscaped and (
|
|
726
|
+
f.concept.isTextBlock or
|
|
727
|
+
(f.value and escapeWorthyStr.match(f.value)))
|
|
728
|
+
if f.value and escapeWorthyStr.match(f.value):
|
|
729
|
+
errorMsg = _("Facts containing special characters like '&' or '<' must be escaped")
|
|
730
|
+
if hasEscapeIssue:
|
|
731
|
+
errorMsg += _(" - fact %(conceptName)s")
|
|
732
|
+
modelXbrl.error("ESEF.2.2.7.improperApplicationOfEscapeAttribute",
|
|
733
|
+
errorMsg,
|
|
734
|
+
modelObject=f, conceptName=f.concept.qname)
|
|
676
735
|
if f.effectiveValue in ["0", "-0"] and f.xValue != 0:
|
|
677
736
|
modelXbrl.warning("ESEF.2.2.5.roundedValueBelowScaleNotNull",
|
|
678
737
|
_("A value that has been rounded and is below the scale should show a value of zero. It has been found to have the value %(value)s - fact %(conceptName)s"),
|
|
@@ -707,6 +766,17 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
707
766
|
#elif dim.isTyped:
|
|
708
767
|
# conceptsUsed.add(dim.typedMember)
|
|
709
768
|
'''
|
|
769
|
+
# identify report date
|
|
770
|
+
reportDate = None
|
|
771
|
+
for f in modelXbrl.factsByLocalName.get("DateOfEndOfReportingPeriod2013", ()):
|
|
772
|
+
if getattr(f, "xValid", 0) >= VALID:
|
|
773
|
+
reportDate = f.xValue
|
|
774
|
+
break
|
|
775
|
+
if not reportDate: # no report period, take name's end context
|
|
776
|
+
for f in modelXbrl.factsByLocalName.get("NameOfReportingEntityOrOtherMeansOfIdentification", ()):
|
|
777
|
+
if getattr(f, "xValid", 0) >= VALID:
|
|
778
|
+
reportDate = f.context.endDate
|
|
779
|
+
break
|
|
710
780
|
|
|
711
781
|
if esefDisclosureSystemYear >= 2024 and factsMissingId:
|
|
712
782
|
modelXbrl.warning("ESEF.2.2.8.missingFactID",
|
|
@@ -776,8 +846,8 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
776
846
|
|
|
777
847
|
if transformRegistryErrors:
|
|
778
848
|
modelXbrl.error("ESEF.2.2.3.incorrectTransformationRuleApplied",
|
|
779
|
-
_("ESMA recommends applying the Transformation Rules Registry
|
|
780
|
-
modelObject=transformRegistryErrors,
|
|
849
|
+
_("ESMA recommends applying the Transformation Rules Registry %(earliestTrNum)s, as published by XBRL International or any more recent versions of the Transformation Rules Registry provided with a 'Recommendation' status, for these elements: %(elements)s."),
|
|
850
|
+
modelObject=transformRegistryErrors, earliestTrNum=earliestTRVersion,
|
|
781
851
|
elements=", ".join(sorted(str(fact.qname) for fact in transformRegistryErrors)))
|
|
782
852
|
|
|
783
853
|
if orphanedFootnotes:
|
|
@@ -833,7 +903,12 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
|
833
903
|
|
|
834
904
|
# 3.1.1 test
|
|
835
905
|
hasOutdatedUrl = False
|
|
836
|
-
|
|
906
|
+
outdatedTaxonomyURLs = val.authParam["outdatedTaxonomyURLs"].copy()
|
|
907
|
+
if reportDate:
|
|
908
|
+
for expiringTaxonomyURLs in val.authParam.get("expiringTaxonomyURLs", ()):
|
|
909
|
+
if expiringTaxonomyURLs["lastReportableDate"] < str(reportDate):
|
|
910
|
+
outdatedTaxonomyURLs.update(expiringTaxonomyURLs["URLs"])
|
|
911
|
+
for e in outdatedTaxonomyURLs:
|
|
837
912
|
if e in val.extensionImportedUrls:
|
|
838
913
|
val.modelXbrl.error("ESEF.3.1.2.incorrectEsefTaxonomyVersionUsed",
|
|
839
914
|
_("The issuer's extension taxonomies MUST import the applicable version of the taxonomy files prepared by ESMA. Outdated entry point: %(url)s"),
|
|
@@ -64,6 +64,7 @@ _: TypeGetText
|
|
|
64
64
|
ESEF_DISCLOSURE_SYSTEM_TEST_PROPERTY = "ESEFplugin"
|
|
65
65
|
|
|
66
66
|
ixErrorPattern = re.compile(r"ix11[.]|xmlSchema[:]|(?!xbrl.5.2.5.2|xbrl.5.2.6.2)xbrl[.]|xbrld[ti]e[:]|utre[:]")
|
|
67
|
+
dupIdErrorPattern = re.compile(r"xml.3.3.1:idMustBeUnique|ix11.14.1.2:uniqueIxId")
|
|
67
68
|
|
|
68
69
|
|
|
69
70
|
def validateEntity(modelXbrl: ModelXbrl, filename:str, filesource: FileSource) -> None:
|
|
@@ -75,7 +76,7 @@ def validateEntity(modelXbrl: ModelXbrl, filename:str, filesource: FileSource) -
|
|
|
75
76
|
parser = XMLParser(load_dtd=True, resolve_entities=False)
|
|
76
77
|
root = parse(file[0], parser=parser)
|
|
77
78
|
if root.docinfo.internalDTD:
|
|
78
|
-
for entity in root.docinfo.internalDTD.iterentities():
|
|
79
|
+
for entity in root.docinfo.internalDTD.iterentities():
|
|
79
80
|
modelXbrl.error(f"{contentOtherThanXHTMLGuidance}.maliciousCodePresent",
|
|
80
81
|
_("Documents MUST NOT contain any malicious content. Dangerous XML entity found: %(element)s."),
|
|
81
82
|
modelObject=filename, element=entity.name)
|
|
@@ -88,6 +89,11 @@ def esefDisclosureSystemSelected(modelXbrl: ModelXbrl) -> bool:
|
|
|
88
89
|
return getattr(modelXbrl.modelManager.disclosureSystem, ESEF_DISCLOSURE_SYSTEM_TEST_PROPERTY, False)
|
|
89
90
|
|
|
90
91
|
|
|
92
|
+
def shouldRunEsefValidationRules(val: ValidateXbrl) -> bool:
|
|
93
|
+
if not val.validateDisclosureSystem:
|
|
94
|
+
return False
|
|
95
|
+
return esefDisclosureSystemSelected(val.modelXbrl)
|
|
96
|
+
|
|
91
97
|
class ESEFPlugin(PluginHooks):
|
|
92
98
|
@staticmethod
|
|
93
99
|
def disclosureSystemTypes(
|
|
@@ -216,7 +222,7 @@ class ESEFPlugin(PluginHooks):
|
|
|
216
222
|
*args: Any,
|
|
217
223
|
**kwargs: Any,
|
|
218
224
|
) -> None:
|
|
219
|
-
if not
|
|
225
|
+
if not shouldRunEsefValidationRules(val):
|
|
220
226
|
return None
|
|
221
227
|
modelXbrl = val.modelXbrl
|
|
222
228
|
val.extensionImportedUrls = set()
|
|
@@ -281,7 +287,7 @@ class ESEFPlugin(PluginHooks):
|
|
|
281
287
|
*args: Any,
|
|
282
288
|
**kwargs: Any,
|
|
283
289
|
) -> None:
|
|
284
|
-
if not
|
|
290
|
+
if not shouldRunEsefValidationRules(val):
|
|
285
291
|
return None
|
|
286
292
|
disclosureSystemYear = getDisclosureSystemYear(val.modelXbrl)
|
|
287
293
|
if disclosureSystemYear == 2021:
|
|
@@ -294,7 +300,7 @@ class ESEFPlugin(PluginHooks):
|
|
|
294
300
|
*args: Any,
|
|
295
301
|
**kwargs: Any,
|
|
296
302
|
) -> None:
|
|
297
|
-
if not
|
|
303
|
+
if not shouldRunEsefValidationRules(val):
|
|
298
304
|
return None
|
|
299
305
|
if val.unconsolidated:
|
|
300
306
|
return None
|
|
@@ -306,6 +312,14 @@ class ESEFPlugin(PluginHooks):
|
|
|
306
312
|
modelObject=modelXbrl)
|
|
307
313
|
return # never loaded properly
|
|
308
314
|
|
|
315
|
+
disclosureSystemYear = getDisclosureSystemYear(modelXbrl)
|
|
316
|
+
if disclosureSystemYear >= 2025:
|
|
317
|
+
numDupIdErrors = sum(dupIdErrorPattern.match(e) is not None for e in modelXbrl.errors if isinstance(e,str))
|
|
318
|
+
if numDupIdErrors:
|
|
319
|
+
modelXbrl.warning("ESEF.2.2.8.duplicatedIdAttribute",
|
|
320
|
+
_("ID attributes should be unique, %(numDupIdErrors)s such errors were reported."),
|
|
321
|
+
modelObject=modelXbrl, numDupIdErrors=numDupIdErrors)
|
|
322
|
+
|
|
309
323
|
numXbrlErrors = sum(ixErrorPattern.match(e) is not None for e in modelXbrl.errors if isinstance(e,str))
|
|
310
324
|
if numXbrlErrors:
|
|
311
325
|
modelXbrl.error("ESEF.RTS.Annex.III.Par.1.invalidInlineXBRL",
|
|
@@ -329,7 +343,7 @@ class ESEFPlugin(PluginHooks):
|
|
|
329
343
|
*args: Any,
|
|
330
344
|
**kwargs: Any,
|
|
331
345
|
) -> None:
|
|
332
|
-
if not
|
|
346
|
+
if not shouldRunEsefValidationRules(val):
|
|
333
347
|
return None
|
|
334
348
|
modelXbrl = val.modelXbrl
|
|
335
349
|
if hasattr(val, 'priorFormulaOptionsRunIDs'): # reset environment formula run IDs if they were saved
|
|
@@ -370,7 +384,7 @@ __pluginInfo__ = {
|
|
|
370
384
|
"Validate ESMA ESEF-2022",
|
|
371
385
|
"validate/ESEF_2022",
|
|
372
386
|
],
|
|
373
|
-
"version": "1.
|
|
387
|
+
"version": "1.2025.00",
|
|
374
388
|
"description": """ESMA ESEF Filer Manual and RTS Validations.""",
|
|
375
389
|
"license": "Apache-2",
|
|
376
390
|
"author": authorLabel,
|