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 CHANGED
@@ -161,6 +161,7 @@ class Cntlr:
161
161
  disable_persistent_config: bool = False,
162
162
  betaFeatures: dict[str, bool] | None = None
163
163
  ) -> None:
164
+ self.logger = None
164
165
  if betaFeatures is None:
165
166
  betaFeatures = {}
166
167
  self.betaFeatures = {
@@ -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
- "Edgar Renderer": "3",
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 = "4"
395
+ group = "6"
394
396
  elif key.startswith("xbrlDB"):
395
- group = "5"
397
+ group = "7"
396
398
  else:
397
- group = "6"
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
- objectUrl = arg.modelDocument.displayUri
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
- LEI_RESULTS = ("valid", "invalid lexical", "invalid checksum")
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
- leiLexicalPattern = re.compile(r"^[0-9A-Z]{18}[0-9]{2}$")
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) -> int:
20
- if validInvalidLeiPattern.match(lei):
21
- return LEI_VALID
22
- if not leiLexicalPattern.match(lei):
23
- return LEI_INVALID_LEXICAL
24
- if not int(
25
- "".join({"0":"0", "1":"1", "2":"2", "3":"3", "4":"4", "5":"5", "6":"6", "7":"7", "8":"8", "9":"9",
26
- "A":"10", "B":"11", "C":"12", "D":"13", "E":"14", "F":"15", "G":"16", "H":"17", "I":"18",
27
- "J":"19", "K":"20", "L":"21", "M":"22", "N":"23", "O":"24", "P":"25", "Q":"26", "R":"27",
28
- "S":"28", "T":"29", "U":"30", "V":"31", "W":"32", "X":"33", "Y":"34", "Z":"35"
29
- }[c] for c in lei)
30
- ) % 97 == 1:
31
- return LEI_INVALID_CHECKSUM
32
- return LEI_VALID
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
- # test cases
36
- for lei, name in (
37
- ("001GPB6A9XPE8XJICC14", "Fidelity Advisor Series I"),
38
- ("004L5FPTUREIWK9T2N63", "Hutchin Hill Capital, LP"),
39
- ("00EHHQ2ZHDCFXJCPCL46", "Vanguard Russell 1000 Growth Index Trust"),
40
- ("00GBW0Z2GYIER7DHDS71", "Aristeia Capital, L.L.C."),
41
- ("1S619D6B3ZQIH6MS6B47", "Barclays Vie SA"),
42
- ("21380014JAZAUFJRHC43", "BRE/OPERA HOLDINGS"),
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
- import os, sys, types, time, ast, importlib, io, json, gettext, traceback
9
- from dataclasses import dataclass
10
- from importlib.metadata import entry_points, EntryPoint
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
- from arelle.Locale import getLanguageCodes
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 io.open(pluginJsonFile, 'rt', encoding='utf-8') as f:
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
- return OrderedDict(
83
- (('modules',OrderedDict((moduleName,
84
- OrderedDict(sorted(moduleInfo.items(),
85
- key=lambda k: {'name': '01',
86
- 'status': '02',
87
- 'version': '03',
88
- 'fileDate': '04', 'version': '05',
89
- 'description': '05',
90
- 'moduleURL': '06',
91
- 'localeURL': '07',
92
- 'localeDomain': '08',
93
- 'license': '09',
94
- 'author': '10',
95
- 'copyright': '11',
96
- 'classMethods': '12'}.get(k[0],k[0]))))
97
- for moduleName, moduleInfo in sorted(pluginConfig['modules'].items()))),
98
- ('classes',OrderedDict(sorted(pluginConfig['classes'].items())))))
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 io.open(pluginJsonFile, 'wt', encoding='utf-8') as f:
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: {}".format(moduleFilename), logging.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
- _moduleImportsSubtree = False
462
+ moduleImportsSubtree = False
430
463
  mergedImportURLs = []
431
464
 
432
- for _url in importURLs:
433
- if _url.startswith("module_import"):
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 _url == "module_import_subtree":
437
- _moduleImportsSubtree = True
438
- elif _url == "module_subtree":
469
+ if url == "module_import_subtree":
470
+ moduleImportsSubtree = True
471
+ elif url == "module_subtree":
439
472
  for _dir in os.listdir(moduleDir):
440
- _subtreeModule = os.path.join(moduleDir,_dir)
441
- if os.path.isdir(_subtreeModule) and _dir != "__pycache__":
442
- mergedImportURLs.append(_subtreeModule)
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(_url)
445
- if parentImportsSubtree and not _moduleImportsSubtree:
446
- _moduleImportsSubtree = True
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 _url in mergedImportURLs:
451
- if isAbsolute(_url) or isLegacyAbs(_url):
452
- _importURL = _url # URL is absolute http or local file system
453
- else: # check if exists relative to this module's directory
454
- _importURL = os.path.join(os.path.dirname(moduleURL), os.path.normpath(_url))
455
- if not os.path.exists(_importURL): # not relative to this plugin, assume standard plugin base
456
- _importURL = _url # moduleModuleInfo adjusts relative URL to plugin base
457
- _importModuleInfo = moduleModuleInfo(moduleURL=_importURL, reload=reload, parentImportsSubtree=_moduleImportsSubtree)
458
- if _importModuleInfo:
459
- _importModuleInfo["isImported"] = True
460
- imports.append(_importModuleInfo)
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 _get_name_dir_prefix(
481
- controller: Cntlr,
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
- if moduleFilename:
496
- if os.path.basename(moduleFilename) == "__init__.py" and os.path.isfile(
497
- moduleFilename
498
- ):
499
- moduleFilename = os.path.dirname(
500
- moduleFilename
501
- ) # want just the dirpart of package
502
-
503
- if os.path.isdir(moduleFilename) and os.path.isfile(
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
- return (moduleName, moduleDir, packageImportPrefix)
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 (None, None, None)
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 IOError:
570
- _gettext = lambda x: x # no translation
597
+ except OSError:
598
+ def _gettext(x):
599
+ return x # no translation
571
600
  else:
572
- _gettext = lambda x: x
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
- for method in pluginMethodsForClass:
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.57'
32
- __version_tuple__ = version_tuple = (2, 37, 57)
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
  Metadata-Version: 2.4
2
2
  Name: arelle-release
3
- Version: 2.37.57
3
+ Version: 2.37.59
4
4
  Summary: An open source XBRL platform.
5
5
  Author-email: "arelle.org" <support@arelle.org>
6
6
  License-Expression: Apache-2.0
@@ -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=nwE_qIQMTa5R3sr_dLP6s3srLhiEA5zDOqfas2pDxoA,31691
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=cr7GWNuYttg7H0pM5vaCPuxgDVgtZzR6ZamX957_1w8,32952
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=F81oi1syHgCFZ99L4e4j7bePGei5oJy34sIYizXd0OE,16762
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=tSPrbQrXEeH5pXgGA_6MAdgMZp20NaW5izJglIXyEQk,5095
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=Gnh7xmvyIQX2PyCwjDMFZVanCM9KW09I-x5x8YfDpJs,42220
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=CcYtsRRssUOvQGQ3FxPpMP9UMr7c8qKJ7JmvTpB0H5U,708
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=2RxNrXqmAqsh7nM9ZNZn6Uo0mTrfFJcb_E_HJw_MLcE,1516
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=wR18Oregy8SBBH2G_u4zqP2_M0EBopx9_AxOZpd1Bao,21358
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=ECWgqzwHA7MZ3g7SeoFI7ttR9Wq_lywV-TlqeUW_juY,3186
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=8m6TkwIKVouENOZ4AmfBd3t_R_oODhEwbYzcsOZfyp4,33878
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=h7-tnCubHme8Meaif-o55TV2rCfMWuikfpZCcK6NNDs,56447
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.57.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
688
- arelle_release-2.37.57.dist-info/METADATA,sha256=2zuOGGta03sWvoswvqhJf2mZeNCt1kZ5M90_ZBKN6g8,9327
689
- arelle_release-2.37.57.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
690
- arelle_release-2.37.57.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
691
- arelle_release-2.37.57.dist-info/top_level.txt,sha256=fwU7SYawL4_r-sUMRg7r1nYVGjFMSDvRWx8VGAXEw7w,7
692
- arelle_release-2.37.57.dist-info/RECORD,,
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,,