arelle-release 2.37.57__py3-none-any.whl → 2.37.59__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/Cntlr.py +1 -0
- arelle/DialogPluginManager.py +6 -4
- arelle/ErrorManager.py +4 -1
- arelle/LeiUtil.py +63 -43
- arelle/PluginManager.py +129 -100
- arelle/_version.py +2 -2
- arelle/plugin/validate/EDINET/Constants.py +19 -0
- arelle/plugin/validate/EDINET/PluginValidationDataExtension.py +31 -0
- arelle/plugin/validate/EDINET/__init__.py +2 -0
- arelle/plugin/validate/EDINET/rules/gfm.py +321 -0
- arelle/plugin/validate/UK/ValidateUK.py +99 -0
- {arelle_release-2.37.57.dist-info → arelle_release-2.37.59.dist-info}/METADATA +1 -1
- {arelle_release-2.37.57.dist-info → arelle_release-2.37.59.dist-info}/RECORD +17 -17
- {arelle_release-2.37.57.dist-info → arelle_release-2.37.59.dist-info}/WHEEL +0 -0
- {arelle_release-2.37.57.dist-info → arelle_release-2.37.59.dist-info}/entry_points.txt +0 -0
- {arelle_release-2.37.57.dist-info → arelle_release-2.37.59.dist-info}/licenses/LICENSE.md +0 -0
- {arelle_release-2.37.57.dist-info → arelle_release-2.37.59.dist-info}/top_level.txt +0 -0
arelle/Cntlr.py
CHANGED
arelle/DialogPluginManager.py
CHANGED
|
@@ -386,15 +386,17 @@ class DialogPluginManager(Toplevel):
|
|
|
386
386
|
group = {
|
|
387
387
|
"ixbrl-viewer": "1", # pip installed Arelle viewer
|
|
388
388
|
"iXBRLViewerPlugin": "2", # git clone installed Arelle viewer
|
|
389
|
-
"
|
|
389
|
+
"EDGAR": "3",
|
|
390
|
+
"Inline XBRL Document Set": "4",
|
|
391
|
+
"XBRL rule processor (xule)": "5",
|
|
390
392
|
}.get(key)
|
|
391
393
|
if not group:
|
|
392
394
|
if key.startswith("Validate"):
|
|
393
|
-
group = "
|
|
395
|
+
group = "6"
|
|
394
396
|
elif key.startswith("xbrlDB"):
|
|
395
|
-
group = "
|
|
397
|
+
group = "7"
|
|
396
398
|
else:
|
|
397
|
-
group = "
|
|
399
|
+
group = "8"
|
|
398
400
|
return group + key.lower()
|
|
399
401
|
|
|
400
402
|
@staticmethod
|
arelle/ErrorManager.py
CHANGED
|
@@ -180,7 +180,10 @@ class ErrorManager:
|
|
|
180
180
|
objectUrl = arg
|
|
181
181
|
else:
|
|
182
182
|
try:
|
|
183
|
-
|
|
183
|
+
if isinstance(arg, ObjectPropertyViewWrapper):
|
|
184
|
+
objectUrl = arg.modelObject.modelDocument.displayUri
|
|
185
|
+
else:
|
|
186
|
+
objectUrl = arg.modelDocument.displayUri
|
|
184
187
|
except AttributeError:
|
|
185
188
|
try:
|
|
186
189
|
objectUrl = arg.displayUri
|
arelle/LeiUtil.py
CHANGED
|
@@ -1,55 +1,75 @@
|
|
|
1
|
-
|
|
1
|
+
"""
|
|
2
2
|
See COPYRIGHT.md for copyright information.
|
|
3
3
|
|
|
4
4
|
Implementation of ISO 17442:2012(E) Appendix A
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import string
|
|
9
|
+
from enum import Enum, auto
|
|
10
|
+
|
|
7
11
|
import regex as re
|
|
8
12
|
|
|
9
|
-
LEI_VALID = 0
|
|
10
|
-
LEI_INVALID_LEXICAL = 1
|
|
11
|
-
LEI_INVALID_CHECKSUM = 2
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
class LEIValidationResult(Enum):
|
|
15
|
+
VALID = auto()
|
|
16
|
+
INVALID_LEXICAL = auto()
|
|
17
|
+
INVALID_CHECKSUM = auto()
|
|
18
|
+
|
|
19
|
+
def description(self) -> str:
|
|
20
|
+
return self.name.lower().replace("_", " ")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
LEI_VALID = LEIValidationResult.VALID
|
|
24
|
+
LEI_INVALID_LEXICAL = LEIValidationResult.INVALID_LEXICAL
|
|
25
|
+
LEI_INVALID_CHECKSUM = LEIValidationResult.INVALID_CHECKSUM
|
|
26
|
+
|
|
27
|
+
_leiLexicalPattern = re.compile(r"[0-9A-Z]{18}[0-9]{2}", re.ASCII)
|
|
28
|
+
_requiredLEILength = 20
|
|
29
|
+
|
|
30
|
+
_leiToDigitTable = str.maketrans(
|
|
31
|
+
dict(
|
|
32
|
+
(ord(char), str(index))
|
|
33
|
+
for index, char in enumerate(string.ascii_uppercase, start=len(string.digits))
|
|
34
|
+
)
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# The pattern comes from Solvency II documentation here https://dev.eiopa.europa.eu/Taxonomy/Full/2.3.0/PF/EIOPA_PensionFunds_Validations_2.3.0_PWD2.xlsx
|
|
38
|
+
_validLeiDespiteChecksumFailPatternString = r"^(029200720E3M3A4D6D01|029200758D5M0AI3F601|315700X8JQ3IU0NGK501|3157007SCCESQAUH5Z01|315700TCC9NTEP7J8Z01|13250000000000000301|315700ADOXDR5PCY5400|315700A0UB9Q7DOQIZ00|315700BM9Z39TNTGQW00|315700BZ5F7DRYG2UM00|315700B8401GTFFY6X01|315700DJ07P6OX10FK01|315700DKCD4QSKLAMO01|315700D23JL5C1DZNT00|315700EIYO2TLSEGQ700|315700ET7M7VQ4C84R00|315700EZSEA51937KX01|315700GXBGM8DBYKHF01|315700GZA843JXKTJ400|315700G5G24XYL1TXH00|315700HS7WJ1B0SUWM01|315700HXOEOK58E58P01|315700HZU4SMI8LZTU00|315700I3W2AFHP8MNQ01|315700JICZ3SY5SAXX00|315700JKZH6I0067ND01|315700JO5E28SRE00Q01|315700JXUHL9H2C3P700|315700LDDN3RM7Y2MP00|315700LWYOZNQ7V1T100|315700MBYPT6PGKO7M01|315700MW2F0KFR45QW01|315700M5843O6DU83901|315700NNMGS8F3P2CN00|315700N0VEIBHP0NPQ01|315700OASRCM664PAW01|315700OFW4YCOBNX4U01|315700OVW93X0T3HP200|315700O666JVNCQU9X00|315700PLI0I7W8IOV400|315700PN3J57ZUNF1V00|315700PXKOSX7WQV4N00|315700P4N9VSLK5QZV01|315700P40OV6BT045900|315700P6TZOLP92KN801|315700P89WR82VNB8Z00|315700QGM4XWZE1I5N01|315700Q1S8O1UORF9700|315700RK8M4FAHMYAP01|315700RTEHY362KXWJ00|315700SNUXK41WMW5J00|315700S22RGYRIEEOT00|315700S3TF79ALV82F01|315700TF5Z7T28HZJK01|315700TWGZ89LLSRS000|315700TXNX10N8XH4K00|315700T2EEQAPBO0C301|315700T6T49EDM16YO01|315700UJ6N4LGKLNPB00|315700UKZXWXEO126601|315700UYFD5GF9R13F01|315700VG7PTE9EJJRX01|315700VITYR7AL4M9S01|315700VMAJZ9JZTXNQ00|315700WH3YMKHCVYW201|315700WKCDF4QGRRO200|315700WR4IHOO1M5LP00|315700WYOZ6994UATN00|315700WZPEIS41QDKE00|315700XEFYMA5EZ0P500|315700XE21UYOA3GAC01|315700XI4Z8GF5BDUJ01|315700XSCP1S8WOD8E01|315700X40GNCOUWJYR00|315700YS6RQ5TF3VBP01|315700Y1W7W1JHAUBW00|315700Y5JNQMMUF5ID01|3157000VAJWZ3P8ZED00|3157001KM8GOU7PXZY01|3157001K2LAL04D87901|3157001MLDD3SDFQA901|3157001TPR6K4GBTLN00|31570010000000006400|31570010000000009601|31570010000000019301|31570010000000025800|31570010000000029001|31570010000000035500|31570010000000038701|31570010000000045200|31570010000000048401|31570010000000054900|31570010000000064600|31570010000000067801|31570010000000077501|31570010000000084000|31570010000000087201|31570010000000096901|31570010000000103400|31570010000000106601|31570010000000116301|31570010000000122800|31570010000000126001|3157002CKDQOCIHE5H01|31570020000000005900|31570029808HJVCNFA01|3157003FQSSGS9OZ9E01|3157004PTVTDOKB46401|3157004R6CH6C1P4KX00|3157005RUI28M8FANK00|3157005VJE7A3MBUS201|3157005WT1SENAE17R00|31570058O0Z320C4GZ00|3157006B6JVZ5DFMSN00|3157006DE3SPNIUY9K01|3157006FR3JBBOLOMX01|3157006I3B6RSTPQLI00|3157006KT1EZ15OIXW00|315700659AALVVLVIO01|31570067WSCDST0S3F01|3157008VKYORMUNC5O01|3157008ZY7CW6LVU5J00|3157009FTHFDDK7FHW01|3157009OVCV07O4HXM00|315700T8U7IU4W8J3A01|315700BBRQHDWX6SHZ00|3157008KD17KROO7UT01)$"
|
|
14
39
|
|
|
15
|
-
|
|
40
|
+
# Turn the _validLeiDespiteChecksumFailPattern in to an immutable set as that is much faster to check
|
|
41
|
+
_validLeiDespiteChecksumFailSet = frozenset(
|
|
42
|
+
_validLeiDespiteChecksumFailPatternString[2:-2].split("|")
|
|
43
|
+
)
|
|
16
44
|
|
|
17
|
-
validInvalidLeiPattern = re.compile("^(029200720E3M3A4D6D01|029200758D5M0AI3F601|315700X8JQ3IU0NGK501|3157007SCCESQAUH5Z01|315700TCC9NTEP7J8Z01|13250000000000000301|315700ADOXDR5PCY5400|315700A0UB9Q7DOQIZ00|315700BM9Z39TNTGQW00|315700BZ5F7DRYG2UM00|315700B8401GTFFY6X01|315700DJ07P6OX10FK01|315700DKCD4QSKLAMO01|315700D23JL5C1DZNT00|315700EIYO2TLSEGQ700|315700ET7M7VQ4C84R00|315700EZSEA51937KX01|315700GXBGM8DBYKHF01|315700GZA843JXKTJ400|315700G5G24XYL1TXH00|315700HS7WJ1B0SUWM01|315700HXOEOK58E58P01|315700HZU4SMI8LZTU00|315700I3W2AFHP8MNQ01|315700JICZ3SY5SAXX00|315700JKZH6I0067ND01|315700JO5E28SRE00Q01|315700JXUHL9H2C3P700|315700LDDN3RM7Y2MP00|315700LWYOZNQ7V1T100|315700MBYPT6PGKO7M01|315700MW2F0KFR45QW01|315700M5843O6DU83901|315700NNMGS8F3P2CN00|315700N0VEIBHP0NPQ01|315700OASRCM664PAW01|315700OFW4YCOBNX4U01|315700OVW93X0T3HP200|315700O666JVNCQU9X00|315700PLI0I7W8IOV400|315700PN3J57ZUNF1V00|315700PXKOSX7WQV4N00|315700P4N9VSLK5QZV01|315700P40OV6BT045900|315700P6TZOLP92KN801|315700P89WR82VNB8Z00|315700QGM4XWZE1I5N01|315700Q1S8O1UORF9700|315700RK8M4FAHMYAP01|315700RTEHY362KXWJ00|315700SNUXK41WMW5J00|315700S22RGYRIEEOT00|315700S3TF79ALV82F01|315700TF5Z7T28HZJK01|315700TWGZ89LLSRS000|315700TXNX10N8XH4K00|315700T2EEQAPBO0C301|315700T6T49EDM16YO01|315700UJ6N4LGKLNPB00|315700UKZXWXEO126601|315700UYFD5GF9R13F01|315700VG7PTE9EJJRX01|315700VITYR7AL4M9S01|315700VMAJZ9JZTXNQ00|315700WH3YMKHCVYW201|315700WKCDF4QGRRO200|315700WR4IHOO1M5LP00|315700WYOZ6994UATN00|315700WZPEIS41QDKE00|315700XEFYMA5EZ0P500|315700XE21UYOA3GAC01|315700XI4Z8GF5BDUJ01|315700XSCP1S8WOD8E01|315700X40GNCOUWJYR00|315700YS6RQ5TF3VBP01|315700Y1W7W1JHAUBW00|315700Y5JNQMMUF5ID01|3157000VAJWZ3P8ZED00|3157001KM8GOU7PXZY01|3157001K2LAL04D87901|3157001MLDD3SDFQA901|3157001TPR6K4GBTLN00|31570010000000006400|31570010000000009601|31570010000000019301|31570010000000025800|31570010000000029001|31570010000000035500|31570010000000038701|31570010000000045200|31570010000000048401|31570010000000054900|31570010000000064600|31570010000000067801|31570010000000077501|31570010000000084000|31570010000000087201|31570010000000096901|31570010000000103400|31570010000000106601|31570010000000116301|31570010000000122800|31570010000000126001|3157002CKDQOCIHE5H01|31570020000000005900|31570029808HJVCNFA01|3157003FQSSGS9OZ9E01|3157004PTVTDOKB46401|3157004R6CH6C1P4KX00|3157005RUI28M8FANK00|3157005VJE7A3MBUS201|3157005WT1SENAE17R00|31570058O0Z320C4GZ00|3157006B6JVZ5DFMSN00|3157006DE3SPNIUY9K01|3157006FR3JBBOLOMX01|3157006I3B6RSTPQLI00|3157006KT1EZ15OIXW00|315700659AALVVLVIO01|31570067WSCDST0S3F01|3157008VKYORMUNC5O01|3157008ZY7CW6LVU5J00|3157009FTHFDDK7FHW01|3157009OVCV07O4HXM00|315700T8U7IU4W8J3A01|315700BBRQHDWX6SHZ00|3157008KD17KROO7UT01)$")
|
|
18
45
|
|
|
19
|
-
def checkLei(lei: str) ->
|
|
20
|
-
if
|
|
21
|
-
return
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
46
|
+
def checkLei(lei: str) -> LEIValidationResult:
|
|
47
|
+
if len(lei) != _requiredLEILength:
|
|
48
|
+
return LEIValidationResult.INVALID_LEXICAL
|
|
49
|
+
|
|
50
|
+
if not _leiLexicalPattern.fullmatch(lei):
|
|
51
|
+
return LEIValidationResult.INVALID_LEXICAL
|
|
52
|
+
|
|
53
|
+
result = LEIValidationResult.INVALID_CHECKSUM
|
|
54
|
+
|
|
55
|
+
if int(lei.translate(_leiToDigitTable)) % 97 == 1:
|
|
56
|
+
result = LEIValidationResult.VALID
|
|
57
|
+
|
|
58
|
+
if (
|
|
59
|
+
result is LEIValidationResult.INVALID_CHECKSUM
|
|
60
|
+
and lei in _validLeiDespiteChecksumFailSet
|
|
61
|
+
):
|
|
62
|
+
result = LEIValidationResult.VALID
|
|
63
|
+
|
|
64
|
+
return result
|
|
65
|
+
|
|
33
66
|
|
|
34
67
|
if __name__ == "__main__":
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
("21380016W7GAG26FIJ74", "SOCIETE FRANCAISE ET SUISSE"),
|
|
44
|
-
("21380058ERUIT9H53T71", "TOTAN ICAP CO., LTD"),
|
|
45
|
-
("213800A9GT65GAES2V60", "BARCLAYS SECURITIES JAPAN LIMITED"),
|
|
46
|
-
("213800DELL1MWFDHVN53", "PIRELLI JAPAN"),
|
|
47
|
-
("213800A9GT65GAES2V60", "BARCLAYS SECURITIES JAPAN LIMITED"),
|
|
48
|
-
("214800A9GT65GAES2V60", "Error 1"),
|
|
49
|
-
("213800A9GT65GAE%2V60", "Error 2"),
|
|
50
|
-
("213800A9GT65GAES2V62", "Error 3"),
|
|
51
|
-
("1234", "Error 4"),
|
|
52
|
-
("""
|
|
53
|
-
5299003M8JKHEFX58Y02""", "Error 4")
|
|
54
|
-
):
|
|
55
|
-
print ("LEI {} result {} name {}".format(lei, LEI_RESULTS[checkLei(lei)], name) )
|
|
68
|
+
import sys
|
|
69
|
+
|
|
70
|
+
leis = sys.argv[1:]
|
|
71
|
+
if not leis:
|
|
72
|
+
raise SystemExit("Specify the LEI(s) you want to check as arguments.")
|
|
73
|
+
|
|
74
|
+
for arg in sys.argv[1:]:
|
|
75
|
+
print(f"{arg:20s} : {checkLei(arg).description()}")
|
arelle/PluginManager.py
CHANGED
|
@@ -1,31 +1,41 @@
|
|
|
1
1
|
'''
|
|
2
2
|
See COPYRIGHT.md for copyright information.
|
|
3
|
-
|
|
4
|
-
based on pull request 4
|
|
5
|
-
|
|
6
3
|
'''
|
|
7
4
|
from __future__ import annotations
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
|
|
6
|
+
import ast
|
|
7
|
+
import gettext
|
|
8
|
+
from glob import glob
|
|
11
9
|
import importlib.util
|
|
10
|
+
import json
|
|
12
11
|
import logging
|
|
13
|
-
|
|
12
|
+
import os
|
|
13
|
+
import sys
|
|
14
|
+
import time
|
|
15
|
+
import traceback
|
|
16
|
+
import types
|
|
17
|
+
from collections import defaultdict
|
|
18
|
+
from collections.abc import Callable, Iterator
|
|
19
|
+
from dataclasses import dataclass
|
|
20
|
+
from importlib.metadata import EntryPoint, entry_points
|
|
21
|
+
from numbers import Number
|
|
22
|
+
from pathlib import Path
|
|
14
23
|
from types import ModuleType
|
|
15
24
|
from typing import TYPE_CHECKING, Any, cast
|
|
16
|
-
|
|
25
|
+
|
|
17
26
|
import arelle.FileSource
|
|
27
|
+
from arelle.Locale import getLanguageCodes
|
|
18
28
|
from arelle.PythonUtil import isLegacyAbs
|
|
29
|
+
from arelle.typing import TypeGetText
|
|
19
30
|
from arelle.UrlUtil import isAbsolute
|
|
20
|
-
from pathlib import Path
|
|
21
|
-
from collections import OrderedDict, defaultdict
|
|
22
|
-
from collections.abc import Callable, Iterator
|
|
23
|
-
|
|
24
31
|
|
|
25
32
|
if TYPE_CHECKING:
|
|
26
33
|
# Prevent potential circular import error
|
|
27
34
|
from .Cntlr import Cntlr
|
|
28
35
|
|
|
36
|
+
|
|
37
|
+
_: TypeGetText
|
|
38
|
+
|
|
29
39
|
PLUGIN_TRACE_FILE = None
|
|
30
40
|
# PLUGIN_TRACE_FILE = "c:/temp/pluginerr.txt"
|
|
31
41
|
PLUGIN_TRACE_LEVEL = logging.WARNING
|
|
@@ -58,7 +68,7 @@ def init(cntlr: Cntlr, loadPluginConfig: bool = True) -> None:
|
|
|
58
68
|
if loadPluginConfig:
|
|
59
69
|
try:
|
|
60
70
|
pluginJsonFile = cntlr.userAppDir + os.sep + "plugins.json"
|
|
61
|
-
with
|
|
71
|
+
with open(pluginJsonFile, encoding='utf-8') as f:
|
|
62
72
|
pluginConfig = json.load(f)
|
|
63
73
|
freshenModuleInfos()
|
|
64
74
|
except Exception:
|
|
@@ -79,29 +89,45 @@ def reset() -> None: # force reloading modules and plugin infos
|
|
|
79
89
|
pluginMethodsForClasses.clear() # dict by class of list of ordered callable function objects
|
|
80
90
|
|
|
81
91
|
def orderedPluginConfig():
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
92
|
+
fieldOrder = [
|
|
93
|
+
'name',
|
|
94
|
+
'status',
|
|
95
|
+
'fileDate',
|
|
96
|
+
'version',
|
|
97
|
+
'description',
|
|
98
|
+
'moduleURL',
|
|
99
|
+
'localeURL',
|
|
100
|
+
'localeDomain',
|
|
101
|
+
'license',
|
|
102
|
+
'author',
|
|
103
|
+
'copyright',
|
|
104
|
+
'classMethods',
|
|
105
|
+
]
|
|
106
|
+
priorityIndex = {k: i for i, k in enumerate(fieldOrder)}
|
|
107
|
+
|
|
108
|
+
def sortModuleInfo(moduleInfo):
|
|
109
|
+
# Prioritize known fields by the index in fieldOrder; sort others alphabetically
|
|
110
|
+
orderedKeys = sorted(
|
|
111
|
+
moduleInfo.keys(),
|
|
112
|
+
key=lambda k: (priorityIndex.get(k, len(priorityIndex)), k)
|
|
113
|
+
)
|
|
114
|
+
return {k: moduleInfo[k] for k in orderedKeys}
|
|
115
|
+
|
|
116
|
+
orderedModules = {
|
|
117
|
+
moduleName: sortModuleInfo(pluginConfig['modules'][moduleName])
|
|
118
|
+
for moduleName in sorted(pluginConfig['modules'].keys())
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
'modules': orderedModules,
|
|
123
|
+
'classes': dict(sorted(pluginConfig['classes'].items()))
|
|
124
|
+
}
|
|
99
125
|
|
|
100
126
|
def save(cntlr: Cntlr) -> None:
|
|
101
127
|
global pluginConfigChanged
|
|
102
128
|
if pluginConfigChanged and cntlr.hasFileSystem and not cntlr.disablePersistentConfig:
|
|
103
129
|
pluginJsonFile = cntlr.userAppDir + os.sep + "plugins.json"
|
|
104
|
-
with
|
|
130
|
+
with open(pluginJsonFile, 'w', encoding='utf-8') as f:
|
|
105
131
|
jsonStr = str(json.dumps(orderedPluginConfig(), ensure_ascii=False, indent=2)) # might not be unicode in 2.7
|
|
106
132
|
f.write(jsonStr)
|
|
107
133
|
pluginConfigChanged = False
|
|
@@ -155,7 +181,6 @@ moduleInfo = {
|
|
|
155
181
|
|
|
156
182
|
'''
|
|
157
183
|
|
|
158
|
-
|
|
159
184
|
def logPluginTrace(message: str, level: Number) -> None:
|
|
160
185
|
"""
|
|
161
186
|
If plugin trace file logging is configured, logs `message` to it.
|
|
@@ -274,6 +299,14 @@ def getModuleFilename(moduleURL: str, reload: bool, normalize: bool, base: str |
|
|
|
274
299
|
if moduleFilename:
|
|
275
300
|
# `moduleFilename` normalized to an existing script
|
|
276
301
|
return moduleFilename, None
|
|
302
|
+
if base and not _isAbsoluteModuleURL(moduleURL):
|
|
303
|
+
# Search for a matching plugin deeper in the plugin directory tree.
|
|
304
|
+
# Handles cases where a plugin exists in a nested structure, such as
|
|
305
|
+
# when a developer clones an entire repository into the plugin directory.
|
|
306
|
+
# Example: arelle/plugin/xule/plugin/xule/__init__.py
|
|
307
|
+
for path in glob("**/" + moduleURL.replace('\\', '/'), recursive=True):
|
|
308
|
+
if normalizedPath := normalizeModuleFilename(path):
|
|
309
|
+
return normalizedPath, None
|
|
277
310
|
# `moduleFilename` did not map to a local filepath or did not normalize to a script
|
|
278
311
|
# Try using `moduleURL` to search for pip-installed entry point
|
|
279
312
|
entryPointRef = EntryPointRef.get(moduleURL)
|
|
@@ -416,7 +449,7 @@ def moduleModuleInfo(
|
|
|
416
449
|
|
|
417
450
|
if moduleFilename:
|
|
418
451
|
try:
|
|
419
|
-
logPluginTrace("Scanning module for plug-in info: {}"
|
|
452
|
+
logPluginTrace(f"Scanning module for plug-in info: {moduleFilename}", logging.INFO)
|
|
420
453
|
moduleInfo = parsePluginInfo(moduleURL, moduleFilename, entryPoint)
|
|
421
454
|
if moduleInfo is None:
|
|
422
455
|
return None
|
|
@@ -426,38 +459,50 @@ def moduleModuleInfo(
|
|
|
426
459
|
del moduleInfo["importURLs"]
|
|
427
460
|
moduleImports = moduleInfo["moduleImports"]
|
|
428
461
|
del moduleInfo["moduleImports"]
|
|
429
|
-
|
|
462
|
+
moduleImportsSubtree = False
|
|
430
463
|
mergedImportURLs = []
|
|
431
464
|
|
|
432
|
-
for
|
|
433
|
-
if
|
|
465
|
+
for url in importURLs:
|
|
466
|
+
if url.startswith("module_import"):
|
|
434
467
|
for moduleImport in moduleImports:
|
|
435
468
|
mergedImportURLs.append(moduleImport + ".py")
|
|
436
|
-
if
|
|
437
|
-
|
|
438
|
-
elif
|
|
469
|
+
if url == "module_import_subtree":
|
|
470
|
+
moduleImportsSubtree = True
|
|
471
|
+
elif url == "module_subtree":
|
|
439
472
|
for _dir in os.listdir(moduleDir):
|
|
440
|
-
|
|
441
|
-
if os.path.isdir(
|
|
442
|
-
mergedImportURLs.append(
|
|
473
|
+
subtreeModule = os.path.join(moduleDir,_dir)
|
|
474
|
+
if os.path.isdir(subtreeModule) and _dir != "__pycache__":
|
|
475
|
+
mergedImportURLs.append(subtreeModule)
|
|
443
476
|
else:
|
|
444
|
-
mergedImportURLs.append(
|
|
445
|
-
if parentImportsSubtree and not
|
|
446
|
-
|
|
477
|
+
mergedImportURLs.append(url)
|
|
478
|
+
if parentImportsSubtree and not moduleImportsSubtree:
|
|
479
|
+
moduleImportsSubtree = True
|
|
447
480
|
for moduleImport in moduleImports:
|
|
448
481
|
mergedImportURLs.append(moduleImport + ".py")
|
|
449
482
|
imports = []
|
|
450
|
-
for
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
483
|
+
for url in mergedImportURLs:
|
|
484
|
+
importURL = url
|
|
485
|
+
if not _isAbsoluteModuleURL(url):
|
|
486
|
+
# Handle relative imports when plugin is loaded from external directory.
|
|
487
|
+
# When EDGAR/render imports EDGAR/validate, this works if EDGAR is in the plugin directory
|
|
488
|
+
# but fails if loaded externally (e.g., dev repo clone at /dev/path/to/EDGAR/).
|
|
489
|
+
# Solution: Find common path segments to resolve /dev/path/to/EDGAR/validate
|
|
490
|
+
# from the importing module at /dev/path/to/EDGAR/render.
|
|
491
|
+
modulePath = Path(moduleFilename)
|
|
492
|
+
importPath = Path(url)
|
|
493
|
+
if importPath.parts:
|
|
494
|
+
importFirstPart = importPath.parts[0]
|
|
495
|
+
for i, modulePathPart in enumerate(reversed(modulePath.parts)):
|
|
496
|
+
if modulePathPart != importFirstPart:
|
|
497
|
+
continue
|
|
498
|
+
# Found a potential branching point, construct and check a new path
|
|
499
|
+
candidateImportURL = str(modulePath.parents[i] / importPath)
|
|
500
|
+
if normalizeModuleFilename(candidateImportURL):
|
|
501
|
+
importURL = candidateImportURL
|
|
502
|
+
importModuleInfo = moduleModuleInfo(moduleURL=importURL, reload=reload, parentImportsSubtree=moduleImportsSubtree)
|
|
503
|
+
if importModuleInfo:
|
|
504
|
+
importModuleInfo["isImported"] = True
|
|
505
|
+
imports.append(importModuleInfo)
|
|
461
506
|
moduleInfo["imports"] = imports
|
|
462
507
|
logPluginTrace(f"Successful module plug-in info: {moduleFilename}", logging.INFO)
|
|
463
508
|
return moduleInfo
|
|
@@ -477,43 +522,30 @@ def moduleInfo(pluginInfo):
|
|
|
477
522
|
moduleInfo.getdefault('classes', []).append(name)
|
|
478
523
|
|
|
479
524
|
|
|
480
|
-
def
|
|
481
|
-
|
|
482
|
-
pluginBase: str,
|
|
483
|
-
moduleURL: str,
|
|
484
|
-
packagePrefix: str = "",
|
|
485
|
-
) -> tuple[str, str, str] | tuple[None, None, None]:
|
|
486
|
-
"""Get the name, directory and prefix of a module."""
|
|
487
|
-
moduleFilename: str
|
|
488
|
-
moduleDir: str
|
|
489
|
-
packageImportPrefix: str
|
|
525
|
+
def _isAbsoluteModuleURL(moduleURL: str) -> bool:
|
|
526
|
+
return isAbsolute(moduleURL) or isLegacyAbs(moduleURL)
|
|
490
527
|
|
|
491
|
-
moduleFilename = controller.webCache.getfilename(
|
|
492
|
-
url=moduleURL, normalize=True, base=pluginBase, allowTransformation=False
|
|
493
|
-
)
|
|
494
528
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
os.path.join(moduleFilename, "__init__.py")
|
|
505
|
-
):
|
|
506
|
-
moduleDir = os.path.dirname(moduleFilename)
|
|
507
|
-
moduleName = os.path.basename(moduleFilename)
|
|
508
|
-
packageImportPrefix = moduleName + "."
|
|
509
|
-
else:
|
|
510
|
-
moduleName = os.path.basename(moduleFilename).partition(".")[0]
|
|
511
|
-
moduleDir = os.path.dirname(moduleFilename)
|
|
512
|
-
packageImportPrefix = packagePrefix
|
|
529
|
+
def _get_name_dir_prefix(modulePath: Path, packagePrefix: str = "") -> tuple[str, str, str] | tuple[None, None, None]:
|
|
530
|
+
"""Get the name, directory and prefix of a module."""
|
|
531
|
+
moduleName = None
|
|
532
|
+
moduleDir = None
|
|
533
|
+
packageImportPrefix = None
|
|
534
|
+
initFileName = "__init__.py"
|
|
535
|
+
|
|
536
|
+
if modulePath.is_file() and modulePath.name == initFileName:
|
|
537
|
+
modulePath = modulePath.parent
|
|
513
538
|
|
|
514
|
-
|
|
539
|
+
if modulePath.is_dir() and (modulePath / initFileName).is_file():
|
|
540
|
+
moduleName = modulePath.name
|
|
541
|
+
moduleDir = str(modulePath.parent)
|
|
542
|
+
packageImportPrefix = moduleName + "."
|
|
543
|
+
elif modulePath.is_file() and modulePath.suffix == ".py":
|
|
544
|
+
moduleName = modulePath.stem
|
|
545
|
+
moduleDir = str(modulePath.parent)
|
|
546
|
+
packageImportPrefix = packagePrefix
|
|
515
547
|
|
|
516
|
-
return (
|
|
548
|
+
return (moduleName, moduleDir, packageImportPrefix)
|
|
517
549
|
|
|
518
550
|
def _get_location(moduleDir: str, moduleName: str) -> str:
|
|
519
551
|
"""Get the file name of a plugin."""
|
|
@@ -543,13 +575,9 @@ def _find_and_load_module(moduleDir: str, moduleName: str) -> ModuleType | None:
|
|
|
543
575
|
def loadModule(moduleInfo: dict[str, Any], packagePrefix: str="") -> None:
|
|
544
576
|
name = moduleInfo['name']
|
|
545
577
|
moduleURL = moduleInfo['moduleURL']
|
|
578
|
+
modulePath = Path(moduleInfo['path'])
|
|
546
579
|
|
|
547
|
-
moduleName, moduleDir, packageImportPrefix = _get_name_dir_prefix(
|
|
548
|
-
controller=_cntlr,
|
|
549
|
-
pluginBase=_pluginBase,
|
|
550
|
-
moduleURL=moduleURL,
|
|
551
|
-
packagePrefix=packagePrefix,
|
|
552
|
-
)
|
|
580
|
+
moduleName, moduleDir, packageImportPrefix = _get_name_dir_prefix(modulePath, packagePrefix)
|
|
553
581
|
|
|
554
582
|
if all(p is None for p in [moduleName, moduleDir, packageImportPrefix]):
|
|
555
583
|
_cntlr.addToLog(message=_ERROR_MESSAGE_IMPORT_TEMPLATE.format(name), level=logging.ERROR)
|
|
@@ -566,10 +594,12 @@ def loadModule(moduleInfo: dict[str, Any], packagePrefix: str="") -> None:
|
|
|
566
594
|
localeDir = os.path.dirname(module.__file__) + os.sep + pluginInfo['localeURL']
|
|
567
595
|
try:
|
|
568
596
|
_gettext = gettext.translation(pluginInfo['localeDomain'], localeDir, getLanguageCodes())
|
|
569
|
-
except
|
|
570
|
-
_gettext
|
|
597
|
+
except OSError:
|
|
598
|
+
def _gettext(x):
|
|
599
|
+
return x # no translation
|
|
571
600
|
else:
|
|
572
|
-
_gettext
|
|
601
|
+
def _gettext(x):
|
|
602
|
+
return x
|
|
573
603
|
for key, value in pluginInfo.items():
|
|
574
604
|
if key == 'name':
|
|
575
605
|
if name:
|
|
@@ -627,8 +657,7 @@ def pluginClassMethods(className: str) -> Iterator[Callable[..., Any]]:
|
|
|
627
657
|
if className in pluginInfo:
|
|
628
658
|
pluginMethodsForClass.append(pluginInfo[className])
|
|
629
659
|
pluginMethodsForClasses[className] = pluginMethodsForClass
|
|
630
|
-
|
|
631
|
-
yield method
|
|
660
|
+
yield from pluginMethodsForClass
|
|
632
661
|
|
|
633
662
|
|
|
634
663
|
def addPluginModule(name: str) -> dict[str, Any] | None:
|
arelle/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '2.37.
|
|
32
|
-
__version_tuple__ = version_tuple = (2, 37,
|
|
31
|
+
__version__ = version = '2.37.59'
|
|
32
|
+
__version_tuple__ = version_tuple = (2, 37, 59)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -58,3 +58,22 @@ PROHIBITED_HTML_TAGS = frozenset({
|
|
|
58
58
|
'select',
|
|
59
59
|
'textarea',
|
|
60
60
|
})
|
|
61
|
+
|
|
62
|
+
NUMERIC_LABEL_ROLES = frozenset({
|
|
63
|
+
'http://www.xbrl.org/2003/role/positiveLabel',
|
|
64
|
+
'http://www.xbrl.org/2003/role/positiveTerseLabel',
|
|
65
|
+
'http://www.xbrl.org/2003/role/positiveVerboseLabel',
|
|
66
|
+
'http://www.xbrl.org/2003/role/negativeLabel',
|
|
67
|
+
'http://www.xbrl.org/2003/role/negativeTerseLabel',
|
|
68
|
+
'http://www.xbrl.org/2003/role/negativeVerboseLabel',
|
|
69
|
+
'http://www.xbrl.org/2003/role/zeroLabel',
|
|
70
|
+
'http://www.xbrl.org/2003/role/zeroTerseLabel',
|
|
71
|
+
'http://www.xbrl.org/2003/role/zeroVerboseLabel',
|
|
72
|
+
'http://www.xbrl.org/2003/role/totalLabel',
|
|
73
|
+
'http://www.xbrl.org/2009/role/negatedLabel',
|
|
74
|
+
'http://www.xbrl.org/2009/role/negatedPeriodEndLabel',
|
|
75
|
+
'http://www.xbrl.org/2009/role/negatedPeriodStartLabel',
|
|
76
|
+
'http://www.xbrl.org/2009/role/negatedTotalLabel',
|
|
77
|
+
'http://www.xbrl.org/2009/role/negatedNetLabel',
|
|
78
|
+
'http://www.xbrl.org/2009/role/negatedTerseLabel',
|
|
79
|
+
})
|
|
@@ -44,6 +44,20 @@ _: TypeGetText
|
|
|
44
44
|
|
|
45
45
|
_DEBIT_QNAME_PATTERN = regex.compile('.*(Liability|Liabilities|Equity)')
|
|
46
46
|
|
|
47
|
+
STANDARD_TAXONOMY_URL_PREFIXES = frozenset((
|
|
48
|
+
'http://disclosure.edinet-fsa.go.jp/taxonomy/',
|
|
49
|
+
'https://disclosure.edinet-fsa.go.jp/taxonomy/',
|
|
50
|
+
'http://www.xbrl.org/20',
|
|
51
|
+
'https://www.xbrl.org/20',
|
|
52
|
+
'http://www.xbrl.org/lrr/',
|
|
53
|
+
'https://www.xbrl.org/lrr/',
|
|
54
|
+
'http://xbrl.org/20',
|
|
55
|
+
'https://xbrl.org/20',
|
|
56
|
+
'http://www.xbrl.org/dtr/',
|
|
57
|
+
'https://www.xbrl.org/dtr/',
|
|
58
|
+
'http://www.w3.org/1999/xlink',
|
|
59
|
+
'https://www.w3.org/1999/xlink'
|
|
60
|
+
))
|
|
47
61
|
|
|
48
62
|
@dataclass(frozen=True)
|
|
49
63
|
class UriReference:
|
|
@@ -175,6 +189,11 @@ class PluginValidationDataExtension(PluginData):
|
|
|
175
189
|
def isCorporateReport(self, modelXbrl: ModelXbrl) -> bool:
|
|
176
190
|
return self.jpcrpNamespace in modelXbrl.namespaceDocs
|
|
177
191
|
|
|
192
|
+
def isExtensionUri(self, uri: str, modelXbrl: ModelXbrl) -> bool:
|
|
193
|
+
if uri.startswith(modelXbrl.uriDir):
|
|
194
|
+
return True
|
|
195
|
+
return not any(uri.startswith(taxonomyUri) for taxonomyUri in STANDARD_TAXONOMY_URL_PREFIXES)
|
|
196
|
+
|
|
178
197
|
@lru_cache(1)
|
|
179
198
|
def isStockForm(self, modelXbrl: ModelXbrl) -> bool:
|
|
180
199
|
formTypes = self.getFormTypes(modelXbrl)
|
|
@@ -183,6 +202,18 @@ class PluginValidationDataExtension(PluginData):
|
|
|
183
202
|
for formType in formTypes
|
|
184
203
|
)
|
|
185
204
|
|
|
205
|
+
@lru_cache(1)
|
|
206
|
+
def getExtensionConcepts(self, modelXbrl: ModelXbrl) -> list[ModelConcept]:
|
|
207
|
+
"""
|
|
208
|
+
Returns a list of extension concepts in the DTS.
|
|
209
|
+
"""
|
|
210
|
+
extensionConcepts = []
|
|
211
|
+
for concepts in modelXbrl.nameConcepts.values():
|
|
212
|
+
for concept in concepts:
|
|
213
|
+
if self.isExtensionUri(concept.qname.namespaceURI, modelXbrl):
|
|
214
|
+
extensionConcepts.append(concept)
|
|
215
|
+
return extensionConcepts
|
|
216
|
+
|
|
186
217
|
def getBalanceSheets(self, modelXbrl: ModelXbrl, statement: Statement) -> list[BalanceSheet]:
|
|
187
218
|
"""
|
|
188
219
|
:return: Balance sheet data for each context/unit pairing the given statement.
|
|
@@ -22,6 +22,8 @@ RELEVELER_MAP: dict[str, dict[str, tuple[str, str | None]]] = {
|
|
|
22
22
|
"xbrl.4.8.2:sharesFactUnit-notSharesMeasure": ("ERROR", None),
|
|
23
23
|
# Silence, duplicated by EDINET.EC5002E
|
|
24
24
|
"xbrl.4.8.2:sharesFactUnit-notSingleMeasure": ("ERROR", None),
|
|
25
|
+
# Silence, duplicated by EDINET.EC5700W.GFM.1.7.2
|
|
26
|
+
"xbrl.5.2.5.2.1:zeroWeight": ("ERROR", None),
|
|
25
27
|
},
|
|
26
28
|
}
|
|
27
29
|
|
|
@@ -10,6 +10,7 @@ from typing import Any, cast, Iterable
|
|
|
10
10
|
import regex
|
|
11
11
|
|
|
12
12
|
from arelle import XbrlConst, XmlUtil
|
|
13
|
+
from arelle.LinkbaseType import LinkbaseType
|
|
13
14
|
from arelle.ModelDtsObject import ModelConcept
|
|
14
15
|
from arelle.ModelInstanceObject import ModelFact
|
|
15
16
|
from arelle.ModelObject import ModelObject
|
|
@@ -27,6 +28,7 @@ from arelle.utils.Units import getDuplicateUnitGroups
|
|
|
27
28
|
from arelle.utils.validate.Decorator import validation
|
|
28
29
|
from arelle.utils.validate.Validation import Validation
|
|
29
30
|
from arelle.utils.validate.ValidationUtil import etreeIterWithDepth
|
|
31
|
+
from ..Constants import NUMERIC_LABEL_ROLES
|
|
30
32
|
from ..DisclosureSystems import (DISCLOSURE_SYSTEM_EDINET)
|
|
31
33
|
from ..PluginValidationDataExtension import PluginValidationDataExtension
|
|
32
34
|
|
|
@@ -702,6 +704,57 @@ def rule_gfm_1_3_20(
|
|
|
702
704
|
)
|
|
703
705
|
|
|
704
706
|
|
|
707
|
+
@validation(
|
|
708
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
709
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
710
|
+
)
|
|
711
|
+
def rule_gfm_1_3_21(
|
|
712
|
+
pluginData: PluginValidationDataExtension,
|
|
713
|
+
val: ValidateXbrl,
|
|
714
|
+
*args: Any,
|
|
715
|
+
**kwargs: Any,
|
|
716
|
+
) -> Iterable[Validation]:
|
|
717
|
+
"""
|
|
718
|
+
EDINET.EC5700W: [GFM 1.3.21] Remove the tuple definition.
|
|
719
|
+
"""
|
|
720
|
+
tupleConcepts = [
|
|
721
|
+
concept for concept in pluginData.getExtensionConcepts(val.modelXbrl)
|
|
722
|
+
if concept.isTuple
|
|
723
|
+
]
|
|
724
|
+
if len(tupleConcepts) > 0:
|
|
725
|
+
yield Validation.warning(
|
|
726
|
+
codes='EDINET.EC5700W.GFM.1.3.21',
|
|
727
|
+
msg=_("Remove the tuple definition."),
|
|
728
|
+
modelObject=tupleConcepts
|
|
729
|
+
)
|
|
730
|
+
|
|
731
|
+
|
|
732
|
+
@validation(
|
|
733
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
734
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
735
|
+
)
|
|
736
|
+
def rule_gfm_1_3_22(
|
|
737
|
+
pluginData: PluginValidationDataExtension,
|
|
738
|
+
val: ValidateXbrl,
|
|
739
|
+
*args: Any,
|
|
740
|
+
**kwargs: Any,
|
|
741
|
+
) -> Iterable[Validation]:
|
|
742
|
+
"""
|
|
743
|
+
EDINET.EC5700W: [GFM 1.3.22] Do not set the xbrldt:typedDomainRef attribute on elements defined in submitter-specific taxonomies.
|
|
744
|
+
"""
|
|
745
|
+
typedDomainConcepts = [
|
|
746
|
+
concept for concept in pluginData.getExtensionConcepts(val.modelXbrl)
|
|
747
|
+
if concept.isTypedDimension
|
|
748
|
+
]
|
|
749
|
+
|
|
750
|
+
if len(typedDomainConcepts) > 0:
|
|
751
|
+
yield Validation.warning(
|
|
752
|
+
codes='EDINET.EC5700W.GFM.1.3.22',
|
|
753
|
+
msg=_("Do not set the xbrldt:typedDomainRef attribute on elements defined in submitter-specific taxonomies."),
|
|
754
|
+
modelObject=typedDomainConcepts
|
|
755
|
+
)
|
|
756
|
+
|
|
757
|
+
|
|
705
758
|
@validation(
|
|
706
759
|
hook=ValidationHook.XBRL_FINALLY,
|
|
707
760
|
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
@@ -886,3 +939,271 @@ def rule_gfm_1_5_8(
|
|
|
886
939
|
label=label.textValue,
|
|
887
940
|
modelObject=label
|
|
888
941
|
)
|
|
942
|
+
|
|
943
|
+
|
|
944
|
+
@validation(
|
|
945
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
946
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
947
|
+
)
|
|
948
|
+
def rule_gfm_1_5_10(
|
|
949
|
+
pluginData: PluginValidationDataExtension,
|
|
950
|
+
val: ValidateXbrl,
|
|
951
|
+
*args: Any,
|
|
952
|
+
**kwargs: Any,
|
|
953
|
+
) -> Iterable[Validation]:
|
|
954
|
+
"""
|
|
955
|
+
EDINET.EC5700W: [GFM 1.5.10] A non-numeric concept should not have a label with a numeric role
|
|
956
|
+
"""
|
|
957
|
+
labelRelationshipSet = val.modelXbrl.relationshipSet(XbrlConst.conceptLabel)
|
|
958
|
+
if labelRelationshipSet is None:
|
|
959
|
+
return
|
|
960
|
+
for concept in val.modelXbrl.qnameConcepts.values():
|
|
961
|
+
if concept.isNumeric:
|
|
962
|
+
continue
|
|
963
|
+
labelRels = labelRelationshipSet.fromModelObject(concept)
|
|
964
|
+
for rel in labelRels:
|
|
965
|
+
label = rel.toModelObject
|
|
966
|
+
if not pluginData.isStandardTaxonomyUrl(label.modelDocument.uri, val.modelXbrl) and label.role in NUMERIC_LABEL_ROLES:
|
|
967
|
+
yield Validation.warning(
|
|
968
|
+
codes='EDINET.EC5700W.GFM.1.5.10',
|
|
969
|
+
msg=_("The non-numeric concept of '%(concept)s' has a label with a numeric role of '%(labelrole)s'"),
|
|
970
|
+
concept=concept.qname,
|
|
971
|
+
labelrole=label.role,
|
|
972
|
+
modelObject=label
|
|
973
|
+
)
|
|
974
|
+
|
|
975
|
+
|
|
976
|
+
@validation(
|
|
977
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
978
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
979
|
+
)
|
|
980
|
+
def rule_gfm_1_6_1(
|
|
981
|
+
pluginData: PluginValidationDataExtension,
|
|
982
|
+
val: ValidateXbrl,
|
|
983
|
+
*args: Any,
|
|
984
|
+
**kwargs: Any,
|
|
985
|
+
) -> Iterable[Validation]:
|
|
986
|
+
"""
|
|
987
|
+
EDINET.EC5700W: [GFM 1.6.1] All presentation relationships must have an order attribute
|
|
988
|
+
"""
|
|
989
|
+
presentationRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.PRESENTATION.getArcroles()))
|
|
990
|
+
if presentationRelationshipSet is None:
|
|
991
|
+
return
|
|
992
|
+
for rel in presentationRelationshipSet.modelRelationships:
|
|
993
|
+
if not rel.arcElement.get("order"):
|
|
994
|
+
yield Validation.warning(
|
|
995
|
+
codes='EDINET.EC5700W.GFM.1.6.1',
|
|
996
|
+
msg=_("The presentation relationship is missing the order attribute"),
|
|
997
|
+
modelObject=rel
|
|
998
|
+
)
|
|
999
|
+
|
|
1000
|
+
|
|
1001
|
+
@validation(
|
|
1002
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
1003
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
1004
|
+
)
|
|
1005
|
+
def rule_gfm_1_6_2(
|
|
1006
|
+
pluginData: PluginValidationDataExtension,
|
|
1007
|
+
val: ValidateXbrl,
|
|
1008
|
+
*args: Any,
|
|
1009
|
+
**kwargs: Any,
|
|
1010
|
+
) -> Iterable[Validation]:
|
|
1011
|
+
"""
|
|
1012
|
+
EDINET.EC5700W: [GFM 1.6.2] Presentation relationships must have unique order attributes
|
|
1013
|
+
"""
|
|
1014
|
+
presentationRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.PRESENTATION.getArcroles()))
|
|
1015
|
+
if presentationRelationshipSet is None:
|
|
1016
|
+
return
|
|
1017
|
+
for modelObject, rels in presentationRelationshipSet.fromModelObjects().items():
|
|
1018
|
+
if len(rels) <= 1:
|
|
1019
|
+
continue
|
|
1020
|
+
relsByOrder = defaultdict(list)
|
|
1021
|
+
for rel in rels:
|
|
1022
|
+
order = rel.arcElement.get("order")
|
|
1023
|
+
if order is not None:
|
|
1024
|
+
relsByOrder[(order, rel.linkrole)].append(rel)
|
|
1025
|
+
for key, orderRels in relsByOrder.items():
|
|
1026
|
+
if len(orderRels) > 1:
|
|
1027
|
+
yield Validation.warning(
|
|
1028
|
+
codes='EDINET.EC5700W.GFM.1.6.2',
|
|
1029
|
+
msg=_("The presentation relationships have the same order attribute: '%(order)s'"),
|
|
1030
|
+
order=key[0],
|
|
1031
|
+
modelObject=orderRels
|
|
1032
|
+
)
|
|
1033
|
+
|
|
1034
|
+
|
|
1035
|
+
@validation(
|
|
1036
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
1037
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
1038
|
+
)
|
|
1039
|
+
def rule_gfm_1_6_5(
|
|
1040
|
+
pluginData: PluginValidationDataExtension,
|
|
1041
|
+
val: ValidateXbrl,
|
|
1042
|
+
*args: Any,
|
|
1043
|
+
**kwargs: Any,
|
|
1044
|
+
) -> Iterable[Validation]:
|
|
1045
|
+
"""
|
|
1046
|
+
EDINET.EC5700W: [GFM 1.6.5] If an element used in an instance is the target in the instance DTS of more than one
|
|
1047
|
+
effective presentation arc in a base set with the same source element, then the
|
|
1048
|
+
presentation arcs must have distinct values of the preferredLabel attribute.
|
|
1049
|
+
"""
|
|
1050
|
+
presentationRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.PRESENTATION.getArcroles()))
|
|
1051
|
+
if presentationRelationshipSet is None:
|
|
1052
|
+
return
|
|
1053
|
+
for modelObject, rels in presentationRelationshipSet.toModelObjects().items():
|
|
1054
|
+
if len(rels) <= 1:
|
|
1055
|
+
continue
|
|
1056
|
+
relsByFrom = defaultdict(list)
|
|
1057
|
+
for rel in rels:
|
|
1058
|
+
relsByFrom[(rel.fromModelObject, rel.preferredLabel, rel.linkrole)].append(rel)
|
|
1059
|
+
for key, fromRels in relsByFrom.items():
|
|
1060
|
+
if len(fromRels) > 1:
|
|
1061
|
+
yield Validation.warning(
|
|
1062
|
+
codes='EDINET.EC5700W.GFM.1.6.5',
|
|
1063
|
+
msg=_("The presentation relationships must have distinct values of the preferredLabel attribute "
|
|
1064
|
+
"when they have the same source and target elements"),
|
|
1065
|
+
modelObject=fromRels
|
|
1066
|
+
)
|
|
1067
|
+
|
|
1068
|
+
|
|
1069
|
+
@validation(
|
|
1070
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
1071
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
1072
|
+
)
|
|
1073
|
+
def rule_gfm_1_7_1(
|
|
1074
|
+
pluginData: PluginValidationDataExtension,
|
|
1075
|
+
val: ValidateXbrl,
|
|
1076
|
+
*args: Any,
|
|
1077
|
+
**kwargs: Any,
|
|
1078
|
+
) -> Iterable[Validation]:
|
|
1079
|
+
"""
|
|
1080
|
+
EDINET.EC5700W: [GFM 1.7.1] All calculation relationships must have an order attribute
|
|
1081
|
+
"""
|
|
1082
|
+
calculationRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.CALCULATION.getArcroles()))
|
|
1083
|
+
if calculationRelationshipSet is None:
|
|
1084
|
+
return
|
|
1085
|
+
for rel in calculationRelationshipSet.modelRelationships:
|
|
1086
|
+
if not rel.arcElement.get("order"):
|
|
1087
|
+
yield Validation.warning(
|
|
1088
|
+
codes='EDINET.EC5700W.GFM.1.7.1',
|
|
1089
|
+
msg=_("The calculation relationship is missing the order attribute"),
|
|
1090
|
+
modelObject=rel
|
|
1091
|
+
)
|
|
1092
|
+
|
|
1093
|
+
|
|
1094
|
+
@validation(
|
|
1095
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
1096
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
1097
|
+
)
|
|
1098
|
+
def rule_gfm_1_7_2(
|
|
1099
|
+
pluginData: PluginValidationDataExtension,
|
|
1100
|
+
val: ValidateXbrl,
|
|
1101
|
+
*args: Any,
|
|
1102
|
+
**kwargs: Any,
|
|
1103
|
+
) -> Iterable[Validation]:
|
|
1104
|
+
"""
|
|
1105
|
+
EDINET.EC5700W: [GFM 1.7.2] All calculation relationships must have a weight of either 1 or -1
|
|
1106
|
+
"""
|
|
1107
|
+
calculationRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.CALCULATION.getArcroles()))
|
|
1108
|
+
if calculationRelationshipSet is None:
|
|
1109
|
+
return
|
|
1110
|
+
for rel in calculationRelationshipSet.modelRelationships:
|
|
1111
|
+
if rel.weight not in [1, -1]:
|
|
1112
|
+
yield Validation.warning(
|
|
1113
|
+
codes='EDINET.EC5700W.GFM.1.7.2',
|
|
1114
|
+
msg=_("The calculation relationship must have a weight of 1 or -1, actual weight: '%(weight)s'"),
|
|
1115
|
+
weight=rel.weight,
|
|
1116
|
+
modelObject=rel
|
|
1117
|
+
)
|
|
1118
|
+
|
|
1119
|
+
|
|
1120
|
+
@validation(
|
|
1121
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
1122
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
1123
|
+
)
|
|
1124
|
+
def rule_gfm_1_7_3(
|
|
1125
|
+
pluginData: PluginValidationDataExtension,
|
|
1126
|
+
val: ValidateXbrl,
|
|
1127
|
+
*args: Any,
|
|
1128
|
+
**kwargs: Any,
|
|
1129
|
+
) -> Iterable[Validation]:
|
|
1130
|
+
"""
|
|
1131
|
+
EDINET.EC5700W: [GFM 1.7.3] The concepts participating in a calculation relationship must have the same period type
|
|
1132
|
+
"""
|
|
1133
|
+
calculationRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.CALCULATION.getArcroles()))
|
|
1134
|
+
if calculationRelationshipSet is None:
|
|
1135
|
+
return
|
|
1136
|
+
for rel in calculationRelationshipSet.modelRelationships:
|
|
1137
|
+
fromConcept = rel.fromModelObject
|
|
1138
|
+
toConcept = rel.toModelObject
|
|
1139
|
+
if fromConcept is not None and toConcept is not None and fromConcept.periodType != toConcept.periodType:
|
|
1140
|
+
yield Validation.warning(
|
|
1141
|
+
codes='EDINET.EC5700W.GFM.1.7.3',
|
|
1142
|
+
msg=_("The concepts participating in a calculation relationship must have the same period types. "
|
|
1143
|
+
"The concept of '%(concept1)s' has a period type of '%(concept1PeriodType)s' and the concept "
|
|
1144
|
+
"of '%(concept2)s' has a period type of '%(concept2PeriodType)s'"),
|
|
1145
|
+
concept1=fromConcept.qname,
|
|
1146
|
+
concept1PeriodType=fromConcept.periodType,
|
|
1147
|
+
concept2=toConcept.qname,
|
|
1148
|
+
concept2PeriodType=toConcept.periodType,
|
|
1149
|
+
modelObject=rel
|
|
1150
|
+
)
|
|
1151
|
+
|
|
1152
|
+
|
|
1153
|
+
@validation(
|
|
1154
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
1155
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
1156
|
+
)
|
|
1157
|
+
def rule_gfm_1_7_6(
|
|
1158
|
+
pluginData: PluginValidationDataExtension,
|
|
1159
|
+
val: ValidateXbrl,
|
|
1160
|
+
*args: Any,
|
|
1161
|
+
**kwargs: Any,
|
|
1162
|
+
) -> Iterable[Validation]:
|
|
1163
|
+
"""
|
|
1164
|
+
EDINET.EC5700W: [GFM 1.7.6] Calculation relationships must have unique order attributes
|
|
1165
|
+
"""
|
|
1166
|
+
calculationRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.CALCULATION.getArcroles()))
|
|
1167
|
+
if calculationRelationshipSet is None:
|
|
1168
|
+
return
|
|
1169
|
+
for modelObject, rels in calculationRelationshipSet.fromModelObjects().items():
|
|
1170
|
+
if len(rels) <= 1:
|
|
1171
|
+
continue
|
|
1172
|
+
relsByOrder = defaultdict(list)
|
|
1173
|
+
for rel in rels:
|
|
1174
|
+
order = rel.arcElement.get("order")
|
|
1175
|
+
if order is not None:
|
|
1176
|
+
relsByOrder[(order, rel.linkrole)].append(rel)
|
|
1177
|
+
for key, orderRels in relsByOrder.items():
|
|
1178
|
+
if len(orderRels) > 1:
|
|
1179
|
+
yield Validation.warning(
|
|
1180
|
+
codes='EDINET.EC5700W.GFM.1.7.6',
|
|
1181
|
+
msg=_("The calculation relationships have the same order attribute: '%(order)s'"),
|
|
1182
|
+
order=key[0],
|
|
1183
|
+
modelObject=orderRels
|
|
1184
|
+
)
|
|
1185
|
+
|
|
1186
|
+
|
|
1187
|
+
@validation(
|
|
1188
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
1189
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
1190
|
+
)
|
|
1191
|
+
def rule_gfm_1_8_1(
|
|
1192
|
+
pluginData: PluginValidationDataExtension,
|
|
1193
|
+
val: ValidateXbrl,
|
|
1194
|
+
*args: Any,
|
|
1195
|
+
**kwargs: Any,
|
|
1196
|
+
) -> Iterable[Validation]:
|
|
1197
|
+
"""
|
|
1198
|
+
EDINET.EC5700W: [GFM 1.8.1] All definition relationships must have an order attribute
|
|
1199
|
+
"""
|
|
1200
|
+
definitionRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.DEFINITION.getArcroles()))
|
|
1201
|
+
if definitionRelationshipSet is None:
|
|
1202
|
+
return
|
|
1203
|
+
for rel in definitionRelationshipSet.modelRelationships:
|
|
1204
|
+
if not rel.arcElement.get("order"):
|
|
1205
|
+
yield Validation.warning(
|
|
1206
|
+
codes='EDINET.EC5700W.GFM.1.8.1',
|
|
1207
|
+
msg=_("The definition relationship is missing the order attribute"),
|
|
1208
|
+
modelObject=rel
|
|
1209
|
+
)
|
|
@@ -25,6 +25,7 @@ CO_AUDIT = 'Co.Audit'
|
|
|
25
25
|
CO_AUDIT_NR = 'Co.AuditNR'
|
|
26
26
|
CO_DIR_REP = 'Co.DirReport'
|
|
27
27
|
CO_DIR_RESP = 'Co.DirResp'
|
|
28
|
+
CO_GROUP = 'Co.Group'
|
|
28
29
|
CO_MED_CO = 'Co.MedCo'
|
|
29
30
|
CO_MICRO = 'Co.Micro'
|
|
30
31
|
CO_MISSING_ELEMENT = 'Co.MissingElement'
|
|
@@ -35,6 +36,7 @@ CO_SEC_477 = 'Co.Sec477'
|
|
|
35
36
|
CO_SEC_480 = 'Co.Sec480'
|
|
36
37
|
LP_ABRID = 'Lp.Abrid'
|
|
37
38
|
LP_AUDIT = 'Lp.Audit'
|
|
39
|
+
LP_GROUP = 'Lp.Group'
|
|
38
40
|
LP_MED_LP = 'Lp.MedLp'
|
|
39
41
|
LP_MEM_RESP = 'Lp.MemResp'
|
|
40
42
|
LP_MICRO = 'Lp.Micro'
|
|
@@ -57,19 +59,23 @@ CONCEPT_ACCOUNTS_STATUS_DIMENSION = 'AccountsStatusDimension'
|
|
|
57
59
|
CONCEPT_ACCOUNTS_TYPE_FULL_OR_ABBREVIATED = 'AccountsTypeFullOrAbbreviated' # DEPRECATED IN 2022+ taxonomies. No replacement yet.
|
|
58
60
|
CONCEPT_ACCOUNTS_TYPE_DIMENSION = 'AccountsTypeDimension'
|
|
59
61
|
CONCEPT_ADVERSE_OPINION = 'AdverseOpinion'
|
|
62
|
+
CONCEPT_BALANCE_SHEET_DATE = 'BalanceSheetDate'
|
|
60
63
|
CONCEPT_CHARITY_FUNDS = 'CharityFunds'
|
|
61
64
|
CONCEPT_CHARITY_REGISTRATION_NUMBER_ENGLAND_WALES = 'CharityRegistrationNumberEnglandWales'
|
|
62
65
|
CONCEPT_CHARITY_REGISTRATION_NUMBER_NORTH_IRELAND = 'CharityRegistrationNumberNorthernIreland'
|
|
63
66
|
CONCEPT_CHARITY_REGISTRATION_NUMBER_SCOTLAND = 'CharityRegistrationNumberScotland'
|
|
67
|
+
CONCEPT_CONSOLIDATED = 'Consolidated'
|
|
64
68
|
CONCEPT_DATE_AUDITOR_REPORT = 'DateAuditorsReport'
|
|
65
69
|
CONCEPT_DATE_CHARITY_AUDITORS_REPORT = 'DateCharityAuditorsReport'
|
|
66
70
|
CONCEPT_DATE_SIGNING_DIRECTOR_REPORT = 'DateSigningDirectorsReport'
|
|
67
71
|
CONCEPT_DATE_SIGNING_TRUSTEES_REPORT = 'DateSigningTrusteesAnnualReport'
|
|
68
72
|
CONCEPT_DIRECTOR_SIGNING_DIRECTORS_REPORT = 'DirectorSigningDirectorsReport'
|
|
69
73
|
CONCEPT_DISCLAIMER_OPINION = 'DisclaimerOpinion'
|
|
74
|
+
CONCEPT_END_DATE_FOR_PERIOD_COVERED_BY_REPORT = 'EndDateForPeriodCoveredByReport'
|
|
70
75
|
CONCEPT_ENTITY_DORMANT = 'EntityDormantTruefalse'
|
|
71
76
|
CONCEPT_ENTITY_TRADING_STATUS = 'EntityTradingStatus'
|
|
72
77
|
CONCEPT_ENTITY_TRADING_STATUS_DIMENSION = 'EntityTradingStatusDimension'
|
|
78
|
+
CONCEPT_GROUP_COMPANY_DATA_DIMENSION = 'GroupCompanyDataDimension'
|
|
73
79
|
CONCEPT_LANGUAGES_DIMENSION = 'LanguagesDimension'
|
|
74
80
|
CONCEPT_MEDIUM_COMPANY = 'StatementThatCompanyHasPreparedAccountsUnderProvisionsRelatingToMedium-sizedCompanies'
|
|
75
81
|
CONCEPT_MEDIUM_COMPANIES_REGIME_FOR_ACCOUNTS = 'Medium-sizedCompaniesRegimeForAccounts'
|
|
@@ -646,6 +652,39 @@ class ValidateUK:
|
|
|
646
652
|
)
|
|
647
653
|
return CodeResult()
|
|
648
654
|
|
|
655
|
+
def _evaluateGroupFacts(self) -> CodeResult:
|
|
656
|
+
"""
|
|
657
|
+
BalanceSheetDate with GroupCompanyDataDimension and Consolidated must equal BalanceSheetDate with default
|
|
658
|
+
dimensions. Both facts must be non-nil.
|
|
659
|
+
"""
|
|
660
|
+
consolidatedFact = None
|
|
661
|
+
defaultFact = None
|
|
662
|
+
endDateFact = None
|
|
663
|
+
for fact in self._getFacts(CONCEPT_END_DATE_FOR_PERIOD_COVERED_BY_REPORT):
|
|
664
|
+
if not fact.context.qnameDims:
|
|
665
|
+
endDateFact = fact
|
|
666
|
+
break
|
|
667
|
+
if endDateFact is None:
|
|
668
|
+
return CodeResult()
|
|
669
|
+
for balanceSheetDateFact in self._getFacts(CONCEPT_BALANCE_SHEET_DATE):
|
|
670
|
+
if self._checkValidFact(balanceSheetDateFact):
|
|
671
|
+
if not balanceSheetDateFact.context.qnameDims and balanceSheetDateFact.context.instantDate == endDateFact.context.instantDate:
|
|
672
|
+
defaultFact = balanceSheetDateFact
|
|
673
|
+
for qname, value in balanceSheetDateFact.context.qnameDims.items():
|
|
674
|
+
if value.xValid < VALID:
|
|
675
|
+
continue
|
|
676
|
+
if qname.localName == CONCEPT_GROUP_COMPANY_DATA_DIMENSION and cast(str, value.xValue.localName) == CONCEPT_CONSOLIDATED:
|
|
677
|
+
consolidatedFact = balanceSheetDateFact
|
|
678
|
+
break
|
|
679
|
+
if consolidatedFact is None or defaultFact is None or consolidatedFact.xValue != defaultFact.xValue:
|
|
680
|
+
return CodeResult(
|
|
681
|
+
conceptLocalName=CONCEPT_BALANCE_SHEET_DATE,
|
|
682
|
+
success=False,
|
|
683
|
+
message="A fact tagged with BalanceSheetDate with the dimension of GroupCompanyDataDimension/Consolidated "
|
|
684
|
+
"must equal a fact tagged with BalanceSheetDate with the default dimension."
|
|
685
|
+
)
|
|
686
|
+
return CodeResult()
|
|
687
|
+
|
|
649
688
|
def _evaluateCode(self, code: str) -> CodeResult:
|
|
650
689
|
"""
|
|
651
690
|
Evaluates whether the conditions associated with the given code pass.
|
|
@@ -667,6 +706,8 @@ class ValidateUK:
|
|
|
667
706
|
result = self._evaluateProfLossOrCharityFundsFact(code)
|
|
668
707
|
elif code == CH_AUDIT:
|
|
669
708
|
result = self._evaluateCharAuditFacts()
|
|
709
|
+
elif code in (CO_GROUP, LP_GROUP):
|
|
710
|
+
result = self._evaluateGroupFacts()
|
|
670
711
|
return self._setCode(code, result)
|
|
671
712
|
|
|
672
713
|
def _getFacts(self, conceptLocalName: str) -> list[ModelFact]:
|
|
@@ -831,6 +872,14 @@ class ValidateUK:
|
|
|
831
872
|
self.validateAuditedOtherLLP()
|
|
832
873
|
else:
|
|
833
874
|
self.validateAuditedOtherCompany()
|
|
875
|
+
elif self.scopeAccounts in {
|
|
876
|
+
ScopeAccounts.GROUP_ONLY.value,
|
|
877
|
+
ScopeAccounts.CONSOLIDATED_GROUP.value,
|
|
878
|
+
}:
|
|
879
|
+
if self.legalFormEntity == CONCEPT_LLP:
|
|
880
|
+
self.validateAuditedGroupLLP()
|
|
881
|
+
else:
|
|
882
|
+
self.validateAuditedGroupCompany()
|
|
834
883
|
|
|
835
884
|
def validateCharities(self) -> None:
|
|
836
885
|
"""
|
|
@@ -1271,3 +1320,53 @@ class ValidateUK:
|
|
|
1271
1320
|
result = self._evaluateCode(CO_SM_CO)
|
|
1272
1321
|
if not result.success:
|
|
1273
1322
|
self._errorOnMissingFactText(CO_SM_CO, result)
|
|
1323
|
+
|
|
1324
|
+
def validateAuditedGroupCompany(self) -> None:
|
|
1325
|
+
"""
|
|
1326
|
+
Checks conditions applicable to audited group companies:
|
|
1327
|
+
Co.Audit AND ((Co.DirReport AND Co.ProfLoss AND Co.Group) OR Co.SmCo).
|
|
1328
|
+
"""
|
|
1329
|
+
result = self._evaluateCode(CO_AUDIT)
|
|
1330
|
+
if not result.success:
|
|
1331
|
+
self._yieldErrorOrWarning(CO_AUDIT, result)
|
|
1332
|
+
|
|
1333
|
+
result = self._evaluateCode(CO_SM_CO)
|
|
1334
|
+
if not result.success:
|
|
1335
|
+
failedOr = False
|
|
1336
|
+
dirRepResult = self._evaluateCode(CO_DIR_REP)
|
|
1337
|
+
if not dirRepResult.success:
|
|
1338
|
+
self._yieldErrorOrWarning(CO_DIR_REP, dirRepResult)
|
|
1339
|
+
failedOr = True
|
|
1340
|
+
profLossResult = self._evaluateCode(CO_PROF_LOSS)
|
|
1341
|
+
if not profLossResult.success:
|
|
1342
|
+
self._yieldErrorOrWarning(CO_PROF_LOSS, profLossResult)
|
|
1343
|
+
failedOr = True
|
|
1344
|
+
groupResult = self._evaluateCode(CO_GROUP)
|
|
1345
|
+
if not groupResult.success:
|
|
1346
|
+
self._yieldErrorOrWarning(CO_GROUP, groupResult)
|
|
1347
|
+
failedOr = True
|
|
1348
|
+
if failedOr:
|
|
1349
|
+
self._errorOnMissingFactText(CO_SM_CO, result)
|
|
1350
|
+
|
|
1351
|
+
def validateAuditedGroupLLP(self) -> None:
|
|
1352
|
+
"""
|
|
1353
|
+
Checks conditions applicable to audited group companies:
|
|
1354
|
+
LP.Audit AND ((LP.ProfLoss+LP.Group) OR LP.SmLp)
|
|
1355
|
+
"""
|
|
1356
|
+
result = self._evaluateCode(LP_AUDIT)
|
|
1357
|
+
if not result.success:
|
|
1358
|
+
self._yieldErrorOrWarning(LP_AUDIT, result)
|
|
1359
|
+
|
|
1360
|
+
result = self._evaluateCode(LP_SM_LP)
|
|
1361
|
+
if not result.success:
|
|
1362
|
+
failedOr = False
|
|
1363
|
+
profLossResult = self._evaluateCode(LP_PROF_LOSS)
|
|
1364
|
+
if not profLossResult.success:
|
|
1365
|
+
self._yieldErrorOrWarning(LP_PROF_LOSS, profLossResult)
|
|
1366
|
+
failedOr = True
|
|
1367
|
+
groupResult = self._evaluateCode(LP_GROUP)
|
|
1368
|
+
if not groupResult.success:
|
|
1369
|
+
self._yieldErrorOrWarning(LP_GROUP, groupResult)
|
|
1370
|
+
failedOr = True
|
|
1371
|
+
if failedOr:
|
|
1372
|
+
self._errorOnMissingFactText(LP_SM_LP, result)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
arelle/Aspect.py,sha256=Pn9I91D1os1RTVj6htuxTfRzVMhmVDtrbKvV_zy9xMI,5470
|
|
2
2
|
arelle/BetaFeatures.py,sha256=T_tPac-FiozHyYLCemt0RoHJ1JahUE71L-0tHmIRKpE,858
|
|
3
|
-
arelle/Cntlr.py,sha256=
|
|
3
|
+
arelle/Cntlr.py,sha256=HQ9JfpByqRf-C-ub07ALWMWUO2LIFev8tcmqpXjHW9s,31718
|
|
4
4
|
arelle/CntlrCmdLine.py,sha256=pOztA3ExK4O-IvxELeBU0SyC4Yk6OKgbsOjpNgwmKiU,88800
|
|
5
5
|
arelle/CntlrComServer.py,sha256=h1KPf31uMbErpxTZn_iklDqUMGFgQnjZkFkFjd8gtLQ,1888
|
|
6
6
|
arelle/CntlrProfiler.py,sha256=2VQJudiUhxryVypxjODx2ccP1-n60icTiWs5lSEokhQ,972
|
|
@@ -16,12 +16,12 @@ arelle/DialogLanguage.py,sha256=HEBd21CfcJwGAGEu2gMyYHM0cXa0VVGAh-kxlZo8H9Q,7228
|
|
|
16
16
|
arelle/DialogNewFactItem.py,sha256=blcE420dyMs6GEJW-YbtPj0lwc-YTUwyyEJh_Zvq9yE,8281
|
|
17
17
|
arelle/DialogOpenArchive.py,sha256=8io1L6JhBVM8dLf23k2q7LsHCO6rpuOvknDmA3H5GSk,24004
|
|
18
18
|
arelle/DialogPackageManager.py,sha256=YyWhHNFZT-ioM5jVbTJyHoQQK6_rB83j3DzMMswQGZk,30540
|
|
19
|
-
arelle/DialogPluginManager.py,sha256=
|
|
19
|
+
arelle/DialogPluginManager.py,sha256=0Uiy2Dei_49K7jbNnEUTchqcjANPIKNYNIt29iQoV1g,33035
|
|
20
20
|
arelle/DialogRssWatch.py,sha256=mjc4pqyFDISY4tQtME0uSRQ3NlcWnNsOsMu9Zj8tTd0,13789
|
|
21
21
|
arelle/DialogURL.py,sha256=JH88OPFf588E8RW90uMaieok7A_4kOAURQ8kHWVhnao,4354
|
|
22
22
|
arelle/DialogUserPassword.py,sha256=kWPlCCihhwvsykDjanME9qBDtv6cxZlsrJyoMqiRep4,13769
|
|
23
23
|
arelle/DisclosureSystem.py,sha256=mQlz8eezPpJuG6gHBV-x4-5Hne3LVh8TQf-Qm9jiFxI,24757
|
|
24
|
-
arelle/ErrorManager.py,sha256=
|
|
24
|
+
arelle/ErrorManager.py,sha256=5BrpD9Ej8b-2JFR-GgWL32Oc2qItYnLbZApaToDKt0o,16972
|
|
25
25
|
arelle/FileSource.py,sha256=asaX2wM47T7S6kELwmXm-YjGIoV6poWz_YdYThY0lpk,47983
|
|
26
26
|
arelle/FunctionCustom.py,sha256=d1FsBG14eykvpLpgaXpN8IdxnlG54dfGcsXPYfdpA9Q,5880
|
|
27
27
|
arelle/FunctionFn.py,sha256=BcZKah1rpfquSVPwjvknM1pgFXOnK4Hr1e_ArG_mcJY,38058
|
|
@@ -32,7 +32,7 @@ arelle/FunctionXs.py,sha256=q0SA0e5wLRxH6kbNUOnChktcGVxaB7A6CE_O3nceysA,13163
|
|
|
32
32
|
arelle/HashUtil.py,sha256=dS11q9UUUFzP7MgiuNdxFfvF3IpeyZUiOFxVFArEEWE,2963
|
|
33
33
|
arelle/HtmlUtil.py,sha256=eXa6yF5xBCOODCuBMY7g8ePZHPOSu6X0gDsW3z9N50A,761
|
|
34
34
|
arelle/InstanceAspectsEvaluator.py,sha256=TePNIs_m0vCIbN5N4PXEyJm529T2WBFi2zmv-72r3Zk,1805
|
|
35
|
-
arelle/LeiUtil.py,sha256=
|
|
35
|
+
arelle/LeiUtil.py,sha256=yWbyU1Qnd81iYHLcyDi0I13GZkjaxCL04YVNQ6jqaP8,4698
|
|
36
36
|
arelle/LinkbaseType.py,sha256=BwRQl4XZFFCopufC2FEMLhYENNTk2JUWVQvnIUsaqtI,3108
|
|
37
37
|
arelle/LocalViewer.py,sha256=WVrfek_bLeFFxgWITi1EQb6xCQN8O9Ks-ZL16vRncSk,3080
|
|
38
38
|
arelle/Locale.py,sha256=07IDxv8nIfvhyRRllFdEAKRI3yo1D2v781cTrjb_RoQ,33193
|
|
@@ -53,7 +53,7 @@ arelle/ModelVersObject.py,sha256=cPD1IzhkCfuV1eMgVFWes88DH_6WkUj5kj7sgGF2M0I,260
|
|
|
53
53
|
arelle/ModelVersReport.py,sha256=bXEA9K3qkH57aABn5l-m3CTY0FAcF1yX6O4fo-URjl8,73326
|
|
54
54
|
arelle/ModelXbrl.py,sha256=oEEP-whMGZjG45BXeXabXcD_IzBp7yA-21gW6zlGe2U,61120
|
|
55
55
|
arelle/PackageManager.py,sha256=BvPExMcxh8rHMxogOag-PGbX6vXdhCiXAHcDLA6Ypsc,32592
|
|
56
|
-
arelle/PluginManager.py,sha256=
|
|
56
|
+
arelle/PluginManager.py,sha256=axNOsdpUvhths9ePeTAF3hurwrOooXA-7cGnG43z99k,42676
|
|
57
57
|
arelle/PluginUtils.py,sha256=0vFQ29wVVpU0cTY3YOBL6FhNQhhCTwShBH4qTJGLnvc,2426
|
|
58
58
|
arelle/PrototypeDtsObject.py,sha256=0lf60VcXR_isx57hBPrc7vEMkFpYkVuK4JVBSmopzkQ,7989
|
|
59
59
|
arelle/PrototypeInstanceObject.py,sha256=CXUoDllhDqpMvSQjqIYi1Ywp-J8fLhQRV9wVD2YXgVo,13204
|
|
@@ -125,7 +125,7 @@ arelle/XmlValidateConst.py,sha256=U_wN0Q-nWKwf6dKJtcu_83FXPn9c6P8JjzGA5b0w7P0,33
|
|
|
125
125
|
arelle/XmlValidateParticles.py,sha256=Mn6vhFl0ZKC_vag1mBwn1rH_x2jmlusJYqOOuxFPO2k,9231
|
|
126
126
|
arelle/XmlValidateSchema.py,sha256=6frtZOc1Yrx_5yYF6V6oHbScnglWrVbWr6xW4EHtLQI,7428
|
|
127
127
|
arelle/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
128
|
-
arelle/_version.py,sha256=
|
|
128
|
+
arelle/_version.py,sha256=OtIOO8dIBzaWUcN4ZpZMg03PbgyJW5ESDOu8koDT508,708
|
|
129
129
|
arelle/typing.py,sha256=PRe-Fxwr2SBqYYUVPCJ3E7ddDX0_oOISNdT5Q97EbRM,1246
|
|
130
130
|
arelle/api/Session.py,sha256=kgSxS7VckA1sQ7xp0pJiK7IK-vRxAdAZKUo8gEx27s8,7549
|
|
131
131
|
arelle/config/creationSoftwareNames.json,sha256=5MK7XUjfDJ9OpRCCHXeOErJ1SlTBZji4WEcEOdOacx0,3128
|
|
@@ -312,19 +312,19 @@ arelle/plugin/validate/DBA/rules/tm.py,sha256=ui9oKBqlAForwkQ9kk9KBiUogTJE5pv1Rb
|
|
|
312
312
|
arelle/plugin/validate/DBA/rules/tr.py,sha256=4TootFjl0HXsKZk1XNBCyj-vnjRs4lg35hfiz_b_4wU,14684
|
|
313
313
|
arelle/plugin/validate/EBA/__init__.py,sha256=x3zXNcdSDJ3kHfL7kMs0Ve0Vs9oWbzNFVf1TK4Avmy8,45924
|
|
314
314
|
arelle/plugin/validate/EBA/config.xml,sha256=37wMVUAObK-XEqakqD8zPNog20emYt4a_yfL1AKubF8,2022
|
|
315
|
-
arelle/plugin/validate/EDINET/Constants.py,sha256=
|
|
315
|
+
arelle/plugin/validate/EDINET/Constants.py,sha256=6LVzBTt4045SxUjeQa37tlN8zYLF1QIz16khYo3eY4A,2418
|
|
316
316
|
arelle/plugin/validate/EDINET/ControllerPluginData.py,sha256=1WhiS0RdrxeXz4pGDzWATEPqCopOh2spr8Z6Qra_Psk,8420
|
|
317
317
|
arelle/plugin/validate/EDINET/CoverPageRequirements.py,sha256=ZR8pk1CQfUIi15as1zVF27W0kRlUF1M_Ygw7appDUno,4488
|
|
318
318
|
arelle/plugin/validate/EDINET/DisclosureSystems.py,sha256=3rKG42Eg-17Xx_KXU_V5yHW6I3LTwQunvf4a44C9k_4,36
|
|
319
319
|
arelle/plugin/validate/EDINET/FilingFormat.py,sha256=SFZ22zFk6RVIA9dpx3iVLlf2heKfZZqt2ZUXUje4BII,18789
|
|
320
320
|
arelle/plugin/validate/EDINET/FormType.py,sha256=jFqjJACJJ4HhkY1t6Fqei0z6rgvH3Mp-dP04KwQVv3Q,2517
|
|
321
321
|
arelle/plugin/validate/EDINET/ManifestInstance.py,sha256=o6BGlaQHSsn6D0VKH4zn59UscKnjTKlo99kSGfGdYlU,6910
|
|
322
|
-
arelle/plugin/validate/EDINET/PluginValidationDataExtension.py,sha256=
|
|
322
|
+
arelle/plugin/validate/EDINET/PluginValidationDataExtension.py,sha256=9YXy4f1EIYq4NGWaOP7dXjVStdtH4Mb4D4wACrIa2PU,22540
|
|
323
323
|
arelle/plugin/validate/EDINET/ReportFolderType.py,sha256=Q-9a-5tJfhK-cjY8WUB2AT1NI-Nn9cFtARVOIJoLRGU,2979
|
|
324
324
|
arelle/plugin/validate/EDINET/Statement.py,sha256=0Mw5IB7LMtvUZ-2xKZfxmq67xF_dCgJo3eNLweLFRHU,9350
|
|
325
325
|
arelle/plugin/validate/EDINET/UploadContents.py,sha256=o29mDoX48M3S2jQqrJO4ZaulltAPt4vD-qdsWTMCUPc,1196
|
|
326
326
|
arelle/plugin/validate/EDINET/ValidationPluginExtension.py,sha256=8LNqvXzNaWP54dShEjet5ely4BnM8ByCSyimKpUx3_s,2577
|
|
327
|
-
arelle/plugin/validate/EDINET/__init__.py,sha256=
|
|
327
|
+
arelle/plugin/validate/EDINET/__init__.py,sha256=T_p2phcMw1lR4J1X4gvqJPEcZNJdJXtaPpc159A-3_8,3298
|
|
328
328
|
arelle/plugin/validate/EDINET/resources/config.xml,sha256=7uT4GcRgk5veMLpFhPPQJxbGKiQvM52P8EMrjn0qd0g,646
|
|
329
329
|
arelle/plugin/validate/EDINET/resources/cover-page-requirements.csv,sha256=8ILKNn8bXbcgG9V0lc8mxorKDKEjJQWLdBQRMvbqtkI,6518
|
|
330
330
|
arelle/plugin/validate/EDINET/resources/edinet-taxonomies.xml,sha256=997I3RGTLg5OY3vn5hQxVFAAxOmDSOYpuyQe6VnWSY0,16285
|
|
@@ -332,7 +332,7 @@ arelle/plugin/validate/EDINET/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQ
|
|
|
332
332
|
arelle/plugin/validate/EDINET/rules/contexts.py,sha256=KPoyWfRaURvxoGVcWP64mTMTAKPMSmQSX06RClCLddw,7590
|
|
333
333
|
arelle/plugin/validate/EDINET/rules/edinet.py,sha256=g3IPMV5-mWbVwVmEwv5-h1LOtdfeBQ9gZI27m744FFw,12628
|
|
334
334
|
arelle/plugin/validate/EDINET/rules/frta.py,sha256=N0YglHYZuLD2IuwE26viR2ViwUYjneBuMFU9vlrS0aQ,7616
|
|
335
|
-
arelle/plugin/validate/EDINET/rules/gfm.py,sha256=
|
|
335
|
+
arelle/plugin/validate/EDINET/rules/gfm.py,sha256=BAcH_yVnJw3abMfsUBk_skZAjB96h-lff795OdnozvQ,45905
|
|
336
336
|
arelle/plugin/validate/EDINET/rules/manifests.py,sha256=MoT9R_a4BzuYdQVbF7RC5wz134Ve68svSdJ3NlpO_AU,4026
|
|
337
337
|
arelle/plugin/validate/EDINET/rules/upload.py,sha256=E_sEhsuUkoH648PGWwpN90uOQMjKNFaZ8priucmYMOk,47908
|
|
338
338
|
arelle/plugin/validate/ESEF/Const.py,sha256=JujF_XV-_TNsxjGbF-8SQS4OOZIcJ8zhCMnr-C1O5Ho,22660
|
|
@@ -370,7 +370,7 @@ arelle/plugin/validate/ROS/config.xml,sha256=ZCpCFgr1ZAjoUuhb1eRpDnmKrae-sXA9yl6
|
|
|
370
370
|
arelle/plugin/validate/ROS/resources/config.xml,sha256=HXWume5HlrAqOx5AtiWWqgADbRatA8YSfm_JvZGwdgQ,657
|
|
371
371
|
arelle/plugin/validate/ROS/rules/__init__.py,sha256=wW7BUAIb7sRkOxC1Amc_ZKrz03FM-Qh1TyZe6wxYaAU,1567
|
|
372
372
|
arelle/plugin/validate/ROS/rules/ros.py,sha256=CRZkZfsKe4y1B-PDkazQ_cD5LRZBk1GJjTscCZXXYUI,21173
|
|
373
|
-
arelle/plugin/validate/UK/ValidateUK.py,sha256=
|
|
373
|
+
arelle/plugin/validate/UK/ValidateUK.py,sha256=UwNR3mhFzWNn6RKLSiC7j3LlMlConOx-9gWGovKamHA,61035
|
|
374
374
|
arelle/plugin/validate/UK/__init__.py,sha256=GT67T_7qpOASzdmgRXFPmLxfPg3Gjubli7luQDK3zck,27462
|
|
375
375
|
arelle/plugin/validate/UK/config.xml,sha256=mUFhWDfBzGTn7v0ZSmf4HaweQTMJh_4ZcJmD9mzCHrA,1547
|
|
376
376
|
arelle/plugin/validate/UK/consistencyChecksByName.json,sha256=BgB9YAWzmcsX-_rU74RBkABwEsS75vrMlwBHsYCz2R0,25247
|
|
@@ -684,9 +684,9 @@ arelle/utils/validate/ValidationUtil.py,sha256=9vmSvShn-EdQy56dfesyV8JjSRVPj7txr
|
|
|
684
684
|
arelle/utils/validate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
685
685
|
arelle/webserver/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
686
686
|
arelle/webserver/bottle.py,sha256=P-JECd9MCTNcxCnKoDUvGcoi03ezYVOgoWgv2_uH-6M,362
|
|
687
|
-
arelle_release-2.37.
|
|
688
|
-
arelle_release-2.37.
|
|
689
|
-
arelle_release-2.37.
|
|
690
|
-
arelle_release-2.37.
|
|
691
|
-
arelle_release-2.37.
|
|
692
|
-
arelle_release-2.37.
|
|
687
|
+
arelle_release-2.37.59.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
|
|
688
|
+
arelle_release-2.37.59.dist-info/METADATA,sha256=wh7XkuU_1WbVuQhpnKA9ybkP_WlbD4eKla0WJ_2md1E,9327
|
|
689
|
+
arelle_release-2.37.59.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
690
|
+
arelle_release-2.37.59.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
|
|
691
|
+
arelle_release-2.37.59.dist-info/top_level.txt,sha256=fwU7SYawL4_r-sUMRg7r1nYVGjFMSDvRWx8VGAXEw7w,7
|
|
692
|
+
arelle_release-2.37.59.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|