arelle-release 2.37.55__py3-none-any.whl → 2.37.56__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/_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.55'
32
- __version_tuple__ = version_tuple = (2, 37, 55)
31
+ __version__ = version = '2.37.56'
32
+ __version_tuple__ = version_tuple = (2, 37, 56)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -27,12 +27,14 @@ _: TypeGetText
27
27
  @dataclass
28
28
  class ControllerPluginData(PluginData):
29
29
  _manifestInstancesById: dict[str, ManifestInstance]
30
+ _uploadContents: UploadContents | None
30
31
  _usedFilepaths: set[Path]
31
32
 
32
33
  def __init__(self, name: str):
33
34
  super().__init__(name)
34
35
  self._manifestInstancesById = {}
35
36
  self._usedFilepaths = set()
37
+ self._uploadContents = None
36
38
 
37
39
  def __hash__(self) -> int:
38
40
  return id(self)
@@ -49,18 +51,21 @@ class ControllerPluginData(PluginData):
49
51
  """
50
52
  return list(self._manifestInstancesById.values())
51
53
 
52
- @lru_cache(1)
53
- def getUploadContents(self, fileSource: FileSource) -> UploadContents:
54
- uploadFilepaths = self.getUploadFilepaths(fileSource)
54
+ def getUploadContents(self) -> UploadContents | None:
55
+ return self._uploadContents
56
+
57
+ def setUploadContents(self, fileSource: FileSource) -> UploadContents:
55
58
  reports = defaultdict(list)
56
59
  uploadPaths = {}
57
- for path in uploadFilepaths:
60
+ for path, zipPath in self.getUploadFilepaths(fileSource).items():
58
61
  if len(path.parts) == 0:
59
62
  continue
63
+ assert isinstance(fileSource.basefile, str)
64
+ fullPath = Path(fileSource.basefile) / path
60
65
  parents = list(reversed([p.name for p in path.parents if len(p.name) > 0]))
61
66
  reportFolderType = None
62
67
  isCorrection = True
63
- isDirectory = len(path.suffix) == 0
68
+ isDirectory = zipPath.is_dir()
64
69
  isInSubdirectory = False
65
70
  reportPath = None
66
71
  if len(parents) > 0:
@@ -79,32 +84,43 @@ class ControllerPluginData(PluginData):
79
84
  if not isCorrection:
80
85
  reports[reportFolderType].append(path)
81
86
  uploadPaths[path] = UploadPathInfo(
87
+ fullPath=fullPath,
82
88
  isAttachment=reportFolderType is not None and reportFolderType.isAttachment,
83
89
  isCorrection=isCorrection,
84
90
  isCoverPage=not isDirectory and path.stem.startswith(Constants.COVER_PAGE_FILENAME_PREFIX),
85
- isDirectory=len(path.suffix) == 0,
91
+ isDirectory=isDirectory,
86
92
  isRoot=len(path.parts) == 1,
87
93
  isSubdirectory=isInSubdirectory or (isDirectory and reportFolderType is not None),
88
94
  path=path,
89
95
  reportFolderType=reportFolderType,
90
96
  reportPath=reportPath,
91
97
  )
92
- return UploadContents(
98
+ self._uploadContents = UploadContents(
93
99
  reports={k: frozenset(v) for k, v in reports.items() if len(v) > 0},
94
- uploadPaths=uploadPaths
100
+ uploadPaths=list(uploadPaths.values())
95
101
  )
102
+ return self._uploadContents
96
103
 
97
104
  @lru_cache(1)
98
- def getUploadFilepaths(self, fileSource: FileSource) -> list[Path]:
105
+ def getUploadFilepaths(self, fileSource: FileSource) -> dict[Path, zipfile.Path]:
99
106
  if not self.isUpload(fileSource):
100
- return []
101
- paths = set()
107
+ return {}
108
+ paths = {}
102
109
  assert isinstance(fileSource.fs, zipfile.ZipFile)
103
- for name in fileSource.fs.namelist():
104
- path = Path(name)
105
- paths.add(path)
106
- paths.update(path.parents)
107
- return sorted(paths)
110
+ # First, fill in paths from zip file list
111
+ for file in fileSource.fs.filelist:
112
+ zipPath = zipfile.Path(fileSource.fs, file.filename)
113
+ paths[Path(file.filename)] = zipPath
114
+ # Then, fill in any parent directories that weren't in file list
115
+ for path in list(paths):
116
+ for parent in path.parents:
117
+ if parent in paths:
118
+ continue
119
+ paths[parent] = zipfile.Path(fileSource.fs, parent.as_posix() + '/')
120
+ return {
121
+ path: paths[path]
122
+ for path in sorted(paths)
123
+ }
108
124
 
109
125
  @lru_cache(1)
110
126
  def getUploadFileSizes(self, fileSource: FileSource) -> dict[Path, int]:
@@ -35,6 +35,7 @@ from .Constants import CORPORATE_FORMS, FormType, xhtmlDtdExtension, PROHIBITED_
35
35
  from .ControllerPluginData import ControllerPluginData
36
36
  from .ManifestInstance import ManifestInstance
37
37
  from .Statement import Statement, STATEMENTS, BalanceSheet, StatementInstance, StatementType
38
+ from .UploadContents import UploadContents
38
39
 
39
40
  _: TypeGetText
40
41
 
@@ -309,7 +310,7 @@ class PluginValidationDataExtension(PluginData):
309
310
  for elt in modelDocument.xmlRootElement.iter():
310
311
  if not isinstance(elt, ModelObject):
311
312
  continue
312
- for attributeName in elt.attrib.keys():
313
+ for attributeName in elt.attrib:
313
314
  if attributeName in PROHIBITED_HTML_ATTRIBUTES:
314
315
  results.append((elt, str(attributeName)))
315
316
  return results
@@ -327,6 +328,10 @@ class PluginValidationDataExtension(PluginData):
327
328
  elts.append(elt)
328
329
  return elts
329
330
 
331
+ def getUploadContents(self, modelXbrl: ModelXbrl) -> UploadContents | None:
332
+ controllerPluginData = ControllerPluginData.get(modelXbrl.modelManager.cntlr, self.name)
333
+ return controllerPluginData.getUploadContents()
334
+
330
335
  @lru_cache(1)
331
336
  def getUriAttributeValues(self, modelDocument: ModelDocument) -> list[tuple[ModelObject, str, str]]:
332
337
  results: list[tuple[ModelObject, str, str]] = []
@@ -4,6 +4,7 @@ See COPYRIGHT.md for copyright information.
4
4
  from __future__ import annotations
5
5
 
6
6
  from dataclasses import dataclass
7
+ from functools import cached_property
7
8
  from pathlib import Path
8
9
 
9
10
  from .ReportFolderType import ReportFolderType
@@ -12,15 +13,30 @@ from .ReportFolderType import ReportFolderType
12
13
  @dataclass(frozen=True)
13
14
  class UploadContents:
14
15
  reports: dict[ReportFolderType, frozenset[Path]]
15
- uploadPaths: dict[Path, UploadPathInfo]
16
+ uploadPaths: list[UploadPathInfo]
16
17
 
17
18
  @property
18
19
  def sortedPaths(self) -> list[Path]:
19
- return sorted(self.uploadPaths.keys())
20
+ return sorted(uploadPath.path for uploadPath in self.uploadPaths)
21
+
22
+ @cached_property
23
+ def uploadPathsByFullPath(self) -> dict[Path, UploadPathInfo]:
24
+ return {
25
+ uploadPath.fullPath: uploadPath
26
+ for uploadPath in self.uploadPaths
27
+ }
28
+
29
+ @cached_property
30
+ def uploadPathsByPath(self) -> dict[Path, UploadPathInfo]:
31
+ return {
32
+ uploadPath.path: uploadPath
33
+ for uploadPath in self.uploadPaths
34
+ }
20
35
 
21
36
 
22
37
  @dataclass(frozen=True)
23
38
  class UploadPathInfo:
39
+ fullPath: Path
24
40
  isAttachment: bool
25
41
  isCorrection: bool
26
42
  isCoverPage: bool
@@ -37,6 +37,7 @@ class ValidationPluginExtension(ValidationPlugin):
37
37
  }, level=logging.INFO
38
38
  )
39
39
  pluginData = ControllerPluginData.get(filesource.cntlr, self.name)
40
+ pluginData.setUploadContents(filesource)
40
41
  entrypointFiles = []
41
42
  for instance in instances:
42
43
  pluginData.addManifestInstance(instance)
@@ -670,3 +670,34 @@ def rule_gfm_1_3_8(
670
670
  msg=_("The submitter-specific taxonomy has an embedded linkbase."),
671
671
  modelObject=embeddedElements
672
672
  )
673
+
674
+
675
+ @validation(
676
+ hook=ValidationHook.XBRL_FINALLY,
677
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
678
+ )
679
+ def rule_gfm_1_5_6(
680
+ pluginData: PluginValidationDataExtension,
681
+ val: ValidateXbrl,
682
+ *args: Any,
683
+ **kwargs: Any,
684
+ ) -> Iterable[Validation]:
685
+ """
686
+ EDINET.EC5700W: [GFM 1.5.6] The length of a label must be less than 511 characters unless it its role is documentation.
687
+ """
688
+ labelRelationshipSet = val.modelXbrl.relationshipSet(XbrlConst.conceptLabel)
689
+ if labelRelationshipSet is None:
690
+ return
691
+ for concept in val.modelXbrl.qnameConcepts.values():
692
+ labelRels = labelRelationshipSet.fromModelObject(concept)
693
+ for rel in labelRels:
694
+ label = rel.toModelObject
695
+ if label.role != XbrlConst.documentationLabel and label.viewText() is not None and len(label.viewText()) > 511:
696
+ yield Validation.warning(
697
+ codes='EDINET.EC5700W.GFM.1.5.6',
698
+ msg=_("The concept of '%(concept)s' has a label classified as '%(role)s' that is longer than 511 characters: %(label)s"),
699
+ concept=concept.qname,
700
+ role=label.role,
701
+ label=label.viewText(),
702
+ modelObject=label
703
+ )
@@ -122,8 +122,10 @@ def rule_EC0100E(
122
122
  For this implementation, we will allow all directories that may be valid for at least one submission type.
123
123
  This allows for a false-negative outcome when a non-correction submission has a correction-only root directory.
124
124
  """
125
- uploadContents = pluginData.getUploadContents(fileSource)
126
- for path, pathInfo in uploadContents.uploadPaths.items():
125
+ uploadContents = pluginData.getUploadContents()
126
+ if uploadContents is None:
127
+ return
128
+ for path, pathInfo in uploadContents.uploadPathsByPath.items():
127
129
  if pathInfo.isRoot and path.name not in ALLOWED_ROOT_FOLDERS:
128
130
  yield Validation.error(
129
131
  codes='EDINET.EC0100E',
@@ -153,8 +155,8 @@ def rule_EC0124E_EC0187E(
153
155
  """
154
156
  uploadFilepaths = pluginData.getUploadFilepaths(fileSource)
155
157
  emptyDirectories = []
156
- for path in uploadFilepaths:
157
- if path.suffix:
158
+ for path, zipPath in uploadFilepaths.items():
159
+ if not zipPath.is_dir():
158
160
  continue
159
161
  if not any(path in p.parents for p in uploadFilepaths):
160
162
  emptyDirectories.append(path)
@@ -227,8 +229,10 @@ def rule_EC0130E(
227
229
  """
228
230
  EDINET.EC0130E: File extensions must match the file extensions allowed in Figure 2-1-3 and Figure 2-1-5.
229
231
  """
230
- uploadContents = pluginData.getUploadContents(fileSource)
231
- for path, pathInfo in uploadContents.uploadPaths.items():
232
+ uploadContents = pluginData.getUploadContents()
233
+ if uploadContents is None:
234
+ return
235
+ for path, pathInfo in uploadContents.uploadPathsByPath.items():
232
236
  if pathInfo.reportFolderType is None or pathInfo.isDirectory:
233
237
  continue
234
238
  validExtensions = pathInfo.reportFolderType.getValidExtensions(pathInfo.isCorrection, pathInfo.isSubdirectory)
@@ -265,7 +269,9 @@ def rule_EC0132E(
265
269
  """
266
270
  EDINET.EC0132E: Store the manifest file directly under the relevant folder.
267
271
  """
268
- uploadContents = pluginData.getUploadContents(fileSource)
272
+ uploadContents = pluginData.getUploadContents()
273
+ if uploadContents is None:
274
+ return
269
275
  for reportFolderType, paths in uploadContents.reports.items():
270
276
  if reportFolderType.isAttachment:
271
277
  continue
@@ -353,8 +359,10 @@ def rule_EC0192E(
353
359
  PublicDoc cover file. Please delete the cover file from PrivateDoc and upload
354
360
  it again.
355
361
  """
356
- uploadContents = pluginData.getUploadContents(fileSource)
357
- for path, pathInfo in uploadContents.uploadPaths.items():
362
+ uploadContents = pluginData.getUploadContents()
363
+ if uploadContents is None:
364
+ return
365
+ for path, pathInfo in uploadContents.uploadPathsByPath.items():
358
366
  if not pathInfo.isCoverPage:
359
367
  continue
360
368
  # Only applies to PrivateDoc correction reports
@@ -383,8 +391,8 @@ def rule_EC0198E(
383
391
  """
384
392
  fileCounts: dict[Path, int] = defaultdict(int)
385
393
  uploadFilepaths = pluginData.getUploadFilepaths(fileSource)
386
- for path in uploadFilepaths:
387
- if len(path.suffix) == 0:
394
+ for path, zipPath in uploadFilepaths.items():
395
+ if zipPath.is_dir():
388
396
  continue
389
397
  for directory in FILE_COUNT_LIMITS.keys():
390
398
  if directory in path.parents:
@@ -421,10 +429,12 @@ def rule_EC0233E(
421
429
  NOTE: This includes files in subdirectories. For example, PublicDoc/00000000_images/image.png
422
430
  comes before PublicDoc/0000000_header_*.htm
423
431
  """
424
- uploadContents = pluginData.getUploadContents(fileSource)
432
+ uploadContents = pluginData.getUploadContents()
433
+ if uploadContents is None:
434
+ return
425
435
  directories = defaultdict(list)
426
436
  for path in uploadContents.sortedPaths:
427
- pathInfo = uploadContents.uploadPaths[path]
437
+ pathInfo = uploadContents.uploadPathsByPath[path]
428
438
  if pathInfo.isDirectory:
429
439
  continue
430
440
  if pathInfo.reportFolderType in (ReportFolderType.PRIVATE_DOC, ReportFolderType.PUBLIC_DOC):
@@ -462,8 +472,10 @@ def rule_EC0234E(
462
472
  """
463
473
  EDINET.EC0234E: A cover file exists in an unsupported subdirectory.
464
474
  """
465
- uploadContents = pluginData.getUploadContents(fileSource)
466
- for path, pathInfo in uploadContents.uploadPaths.items():
475
+ uploadContents = pluginData.getUploadContents()
476
+ if uploadContents is None:
477
+ return
478
+ for path, pathInfo in uploadContents.uploadPathsByPath.items():
467
479
  if pathInfo.isDirectory:
468
480
  continue
469
481
  if pathInfo.reportFolderType not in (ReportFolderType.PRIVATE_DOC, ReportFolderType.PUBLIC_DOC):
@@ -549,14 +561,16 @@ def rule_EC0349E(
549
561
  EDINET.EC0349E: An unexpected directory or file exists in the XBRL directory.
550
562
  Only PublicDoc, PrivateDoc, or AuditDoc directories may exist beneath the XBRL directory.
551
563
  """
552
- uploadContent = pluginData.getUploadContents(fileSource)
564
+ uploadContents = pluginData.getUploadContents()
565
+ if uploadContents is None:
566
+ return
553
567
  xbrlDirectoryPath = Path('XBRL')
554
568
  allowedPaths = {p.xbrlDirectory for p in (
555
569
  ReportFolderType.AUDIT_DOC,
556
570
  ReportFolderType.PRIVATE_DOC,
557
571
  ReportFolderType.PUBLIC_DOC,
558
572
  )}
559
- for path, pathInfo in uploadContent.uploadPaths.items():
573
+ for path, pathInfo in uploadContents.uploadPathsByPath.items():
560
574
  if path.parent != xbrlDirectoryPath:
561
575
  continue
562
576
  if path not in allowedPaths:
@@ -583,8 +597,10 @@ def rule_EC0352E(
583
597
  """
584
598
  EDINET.EC0352E: An XBRL file with an invalid name exists.
585
599
  """
586
- uploadContent = pluginData.getUploadContents(fileSource)
587
- for path, pathInfo in uploadContent.uploadPaths.items():
600
+ uploadContents = pluginData.getUploadContents()
601
+ if uploadContents is None:
602
+ return
603
+ for path, pathInfo in uploadContents.uploadPathsByPath.items():
588
604
  if (
589
605
  pathInfo.isDirectory or
590
606
  pathInfo.isCorrection or
@@ -603,6 +619,43 @@ def rule_EC0352E(
603
619
  )
604
620
 
605
621
 
622
+ @validation(
623
+ hook=ValidationHook.XBRL_FINALLY,
624
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
625
+ )
626
+ def rules_cover_page(
627
+ pluginData: PluginValidationDataExtension,
628
+ val: ValidateXbrl,
629
+ *args: Any,
630
+ **kwargs: Any,
631
+ ) -> Iterable[Validation]:
632
+ """
633
+ EDINET.EC1000E: Cover page must contain "【表紙】".
634
+ """
635
+ uploadContents = pluginData.getUploadContents(val.modelXbrl)
636
+ if uploadContents is None:
637
+ return
638
+ for url, doc in val.modelXbrl.urlDocs.items():
639
+ path = Path(url)
640
+ pathInfo = uploadContents.uploadPathsByFullPath.get(path)
641
+ if pathInfo is None or not pathInfo.isCoverPage:
642
+ continue
643
+ rootElt = doc.xmlRootElement
644
+ coverPageTextFound = False
645
+ for elt in rootElt.iterdescendants():
646
+ if not coverPageTextFound and elt.text and '【表紙】' in elt.text:
647
+ coverPageTextFound = True
648
+ # TODO: Other checks related to the cover page will be implemented here.
649
+ if not coverPageTextFound:
650
+ yield Validation.error(
651
+ codes='EDINET.EC1000E',
652
+ msg=_("There is no '【表紙】' on the cover page. "
653
+ "File name: '%(file)s'. "
654
+ "Please add '【表紙】' to the relevant file."),
655
+ file=doc.basename,
656
+ )
657
+
658
+
606
659
  @validation(
607
660
  hook=ValidationHook.XBRL_FINALLY,
608
661
  disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
@@ -644,8 +697,13 @@ def rule_uri_references(
644
697
  """
645
698
  EDINET.EC1007E: The URI in the HTML specifies a URL or absolute path.
646
699
  EDINET.EC1013E: The URI in the HTML specifies a path not under a subdirectory.
647
- EDINET.EC1014E: The URI in the HTML specifies a path to a file that doesn't exist.
700
+ EDINET.EC1014E: The URI in the HTML specifies a path to a directory.
701
+ EDINET.EC1021E: The URI in the HTML specifies a path to a file that doesn't exist.
702
+ EDINET.EC1023E: The URI in the HTML specifies a path to a PDF file.
648
703
  """
704
+ uploadContents = pluginData.getUploadContents(val.modelXbrl)
705
+ if uploadContents is None:
706
+ return
649
707
  for uriReference in pluginData.uriReferences:
650
708
  if UrlUtil.isAbsolute(uriReference.attributeValue):
651
709
  yield Validation.error(
@@ -671,10 +729,11 @@ def rule_uri_references(
671
729
  )
672
730
  continue
673
731
  fullPath = Path(uriReference.document.uri).parent / path
674
- if not val.modelXbrl.fileSource.exists(str(fullPath)):
732
+ pathInfo = uploadContents.uploadPathsByFullPath.get(fullPath)
733
+ if pathInfo is not None and pathInfo.isDirectory:
675
734
  yield Validation.error(
676
735
  codes='EDINET.EC1014E',
677
- msg=_("The URI in the HTML specifies a path to a file that doesn't exist. "
736
+ msg=_("The URI in the HTML specifies a path to a directory. "
678
737
  "File name: '%(file)s' (line %(line)s). "
679
738
  "Please update the URI to reference a file."),
680
739
  file=uriReference.document.basename,
@@ -682,6 +741,29 @@ def rule_uri_references(
682
741
  modelObject=uriReference.element,
683
742
  )
684
743
  continue
744
+ if path.suffix.lower() == '.pdf':
745
+ yield Validation.error(
746
+ codes='EDINET.EC1023E',
747
+ msg=_("The URI in the HTML specifies a path to a PDF file. "
748
+ "File name: '%(file)s' (line %(line)s). "
749
+ "Please remove the link from the relevant file."),
750
+ file=uriReference.document.basename,
751
+ line=uriReference.element.sourceline,
752
+ modelObject=uriReference.element,
753
+ )
754
+ continue
755
+ if not val.modelXbrl.fileSource.exists(str(fullPath)):
756
+ yield Validation.error(
757
+ codes='EDINET.EC1021E',
758
+ msg=_("The linked file ('%(path)s') does not exist. "
759
+ "File name: '%(file)s' (line %(line)s). "
760
+ "Please update the URI to reference a file."),
761
+ path=str(path),
762
+ file=uriReference.document.basename,
763
+ line=uriReference.element.sourceline,
764
+ modelObject=uriReference.element,
765
+ )
766
+ continue
685
767
 
686
768
 
687
769
  @validation(
@@ -727,10 +809,12 @@ def rule_EC1017E(
727
809
  """
728
810
  EDINET.EC1017E: There is an unused file.
729
811
  """
730
- uploadContents = pluginData.getUploadContents(fileSource)
812
+ uploadContents = pluginData.getUploadContents()
813
+ if uploadContents is None:
814
+ return
731
815
  existingSubdirectoryFilepaths = {
732
816
  path
733
- for path, pathInfo in uploadContents.uploadPaths.items()
817
+ for path, pathInfo in uploadContents.uploadPathsByPath.items()
734
818
  if pathInfo.isSubdirectory and not pathInfo.isDirectory
735
819
  }
736
820
  usedFilepaths = pluginData.getUsedFilepaths()
@@ -810,7 +894,7 @@ def rule_EC1031E(
810
894
  msg=_("Prohibited attribute '%(attributeName)s' is used in HTML. "
811
895
  "File name: %(file)s (line %(line)s). "
812
896
  "Please correct the tag attributes of the relevant file."),
813
- attributeName=elt.qname.localName,
897
+ attributeName=attributeName,
814
898
  file=doc.basename,
815
899
  line=elt.sourceline,
816
900
  modelObject=elt,
@@ -838,7 +922,10 @@ def rule_filenames(
838
922
  than those allowed (alphanumeric characters, '-' and '_').
839
923
  Note: Applies ONLY to files directly beneath non-correction report folders.
840
924
  """
841
- for path, pathInfo in pluginData.getUploadContents(fileSource).uploadPaths.items():
925
+ uploadContents = pluginData.getUploadContents()
926
+ if uploadContents is None:
927
+ return
928
+ for path, pathInfo in uploadContents.uploadPathsByPath.items():
842
929
  isReportFile = (
843
930
  not pathInfo.isAttachment and
844
931
  not pathInfo.isCorrection and
@@ -302,14 +302,14 @@ def rule_ros19(
302
302
  equity_facts = val.modelXbrl.factsByLocalName.get(EQUITY, set())
303
303
  largest_equity_fact = None
304
304
  for e_fact in equity_facts:
305
- if e_fact.xValid >= VALID:
305
+ if e_fact.xValid >= VALID and e_fact.xValue is not None:
306
306
  if largest_equity_fact is None or abs(convertToDecimal(e_fact.xValue)) > abs(convertToDecimal(largest_equity_fact.xValue)):
307
307
  largest_equity_fact = e_fact
308
308
 
309
309
  turnover_facts = val.modelXbrl.factsByLocalName.get(TURNOVER_REVENUE, set())
310
310
  largest_turnover_fact = None
311
311
  for t_fact in turnover_facts:
312
- if t_fact.xValid >= VALID:
312
+ if t_fact.xValid >= VALID and t_fact.xValue is not None:
313
313
  if largest_turnover_fact is None or convertToDecimal(t_fact.xValue) > convertToDecimal(largest_turnover_fact.xValue):
314
314
  largest_turnover_fact = t_fact
315
315
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arelle-release
3
- Version: 2.37.55
3
+ Version: 2.37.56
4
4
  Summary: An open source XBRL platform.
5
5
  Author-email: "arelle.org" <support@arelle.org>
6
6
  License-Expression: Apache-2.0
@@ -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=Ob4Yjv_aHEp_grPDRfpg4fr4W3Qb5_yliBYKjfPLi7w,708
128
+ arelle/_version.py,sha256=fDTO72M4k2Bg8Q2mmpC5_JjetoiOyfrQn3nJSak1r6w,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
@@ -313,14 +313,14 @@ arelle/plugin/validate/DBA/rules/tr.py,sha256=4TootFjl0HXsKZk1XNBCyj-vnjRs4lg35h
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
315
  arelle/plugin/validate/EDINET/Constants.py,sha256=IkJ1R0ZQAs9kUXY8eB7e8azYlC4m500VILrQhniB5Q4,1949
316
- arelle/plugin/validate/EDINET/ControllerPluginData.py,sha256=2hMPxpL0vZqmzcuvPl9o4_g79ZOtJgLI1p_VRW2YYVk,7259
316
+ arelle/plugin/validate/EDINET/ControllerPluginData.py,sha256=zsBOWkSatXza0v_ZB-jIdFLSIj9Btzd50GdNIgVw6Fo,8027
317
317
  arelle/plugin/validate/EDINET/DisclosureSystems.py,sha256=3rKG42Eg-17Xx_KXU_V5yHW6I3LTwQunvf4a44C9k_4,36
318
318
  arelle/plugin/validate/EDINET/ManifestInstance.py,sha256=o6BGlaQHSsn6D0VKH4zn59UscKnjTKlo99kSGfGdYlU,6910
319
- arelle/plugin/validate/EDINET/PluginValidationDataExtension.py,sha256=aM3OtB0X6NSHouFDut68pkLfe143rTW6nL6tIcI84D4,16092
319
+ arelle/plugin/validate/EDINET/PluginValidationDataExtension.py,sha256=CVTtWTYrvC_oeK9iWHLt5l9dN3KmzgLAEZmGdlPqv3g,16362
320
320
  arelle/plugin/validate/EDINET/ReportFolderType.py,sha256=Q-9a-5tJfhK-cjY8WUB2AT1NI-Nn9cFtARVOIJoLRGU,2979
321
321
  arelle/plugin/validate/EDINET/Statement.py,sha256=0Mw5IB7LMtvUZ-2xKZfxmq67xF_dCgJo3eNLweLFRHU,9350
322
- arelle/plugin/validate/EDINET/UploadContents.py,sha256=IKQYl6lXYTfAZKLIDzRRGSRt3FXoL2Eldbx3Dh7T2I4,712
323
- arelle/plugin/validate/EDINET/ValidationPluginExtension.py,sha256=xr4Ft9v-zpwgRuKSjregyHjtUb5KgHqdSx9NKg9UVeg,2528
322
+ arelle/plugin/validate/EDINET/UploadContents.py,sha256=o29mDoX48M3S2jQqrJO4ZaulltAPt4vD-qdsWTMCUPc,1196
323
+ arelle/plugin/validate/EDINET/ValidationPluginExtension.py,sha256=8LNqvXzNaWP54dShEjet5ely4BnM8ByCSyimKpUx3_s,2577
324
324
  arelle/plugin/validate/EDINET/__init__.py,sha256=ECWgqzwHA7MZ3g7SeoFI7ttR9Wq_lywV-TlqeUW_juY,3186
325
325
  arelle/plugin/validate/EDINET/resources/config.xml,sha256=7uT4GcRgk5veMLpFhPPQJxbGKiQvM52P8EMrjn0qd0g,646
326
326
  arelle/plugin/validate/EDINET/resources/edinet-taxonomies.xml,sha256=997I3RGTLg5OY3vn5hQxVFAAxOmDSOYpuyQe6VnWSY0,16285
@@ -328,9 +328,9 @@ arelle/plugin/validate/EDINET/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQ
328
328
  arelle/plugin/validate/EDINET/rules/contexts.py,sha256=KPoyWfRaURvxoGVcWP64mTMTAKPMSmQSX06RClCLddw,7590
329
329
  arelle/plugin/validate/EDINET/rules/edinet.py,sha256=VYrDZaKbsQuQEvOY5F0Pv4Jzk9YZ4iETOkAFOggrhEY,12632
330
330
  arelle/plugin/validate/EDINET/rules/frta.py,sha256=N0YglHYZuLD2IuwE26viR2ViwUYjneBuMFU9vlrS0aQ,7616
331
- arelle/plugin/validate/EDINET/rules/gfm.py,sha256=3kV7jahj_Y8GxwgjebUtnIrnDTbZEYEDfzokDTGcehc,25490
331
+ arelle/plugin/validate/EDINET/rules/gfm.py,sha256=1SBUQG2M4VOhxp4e2ALnm_Uq8p3g-S0WbKkRCEH-ttw,26769
332
332
  arelle/plugin/validate/EDINET/rules/manifests.py,sha256=MoT9R_a4BzuYdQVbF7RC5wz134Ve68svSdJ3NlpO_AU,4026
333
- arelle/plugin/validate/EDINET/rules/upload.py,sha256=f9MLg7Dz9czsAHKE-SQHRlCe77AXWAPyp4RDeB0G8Rc,36818
333
+ arelle/plugin/validate/EDINET/rules/upload.py,sha256=opGEEFf80RLKk_Z43gkUYHJLBvq_Hd-BMzMMg2Vhnns,40064
334
334
  arelle/plugin/validate/ESEF/Const.py,sha256=JujF_XV-_TNsxjGbF-8SQS4OOZIcJ8zhCMnr-C1O5Ho,22660
335
335
  arelle/plugin/validate/ESEF/Dimensions.py,sha256=MOJM7vwNPEmV5cu-ZzPrhx3347ZvxgD6643OB2HRnIk,10597
336
336
  arelle/plugin/validate/ESEF/Util.py,sha256=QH3btcGqBpr42M7WSKZLSdNXygZaZLfEiEjlxoG21jE,7950
@@ -365,7 +365,7 @@ arelle/plugin/validate/ROS/__init__.py,sha256=KuWg1MHVzA2S6eaHFptvP3Vu_5gQWf3OUY
365
365
  arelle/plugin/validate/ROS/config.xml,sha256=ZCpCFgr1ZAjoUuhb1eRpDnmKrae-sXA9yl6SOWnrfm8,654
366
366
  arelle/plugin/validate/ROS/resources/config.xml,sha256=HXWume5HlrAqOx5AtiWWqgADbRatA8YSfm_JvZGwdgQ,657
367
367
  arelle/plugin/validate/ROS/rules/__init__.py,sha256=wW7BUAIb7sRkOxC1Amc_ZKrz03FM-Qh1TyZe6wxYaAU,1567
368
- arelle/plugin/validate/ROS/rules/ros.py,sha256=Dk5BkfKQYItImdx5FcFvkMWT5BlJ1r_L7Vn-EsCG85A,19870
368
+ arelle/plugin/validate/ROS/rules/ros.py,sha256=yGvvJZJwENgFLpA5r464eez1aHf3pKZm0HyzaWiQn6E,19930
369
369
  arelle/plugin/validate/UK/ValidateUK.py,sha256=h7-tnCubHme8Meaif-o55TV2rCfMWuikfpZCcK6NNDs,56447
370
370
  arelle/plugin/validate/UK/__init__.py,sha256=GT67T_7qpOASzdmgRXFPmLxfPg3Gjubli7luQDK3zck,27462
371
371
  arelle/plugin/validate/UK/config.xml,sha256=mUFhWDfBzGTn7v0ZSmf4HaweQTMJh_4ZcJmD9mzCHrA,1547
@@ -680,9 +680,9 @@ arelle/utils/validate/ValidationUtil.py,sha256=9vmSvShn-EdQy56dfesyV8JjSRVPj7txr
680
680
  arelle/utils/validate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
681
681
  arelle/webserver/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
682
682
  arelle/webserver/bottle.py,sha256=P-JECd9MCTNcxCnKoDUvGcoi03ezYVOgoWgv2_uH-6M,362
683
- arelle_release-2.37.55.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
684
- arelle_release-2.37.55.dist-info/METADATA,sha256=7zIMq7-ff7-wq1Vq8wfHYzjd4A_sMGZsPHuiIz2-nNY,9327
685
- arelle_release-2.37.55.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
686
- arelle_release-2.37.55.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
687
- arelle_release-2.37.55.dist-info/top_level.txt,sha256=fwU7SYawL4_r-sUMRg7r1nYVGjFMSDvRWx8VGAXEw7w,7
688
- arelle_release-2.37.55.dist-info/RECORD,,
683
+ arelle_release-2.37.56.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
684
+ arelle_release-2.37.56.dist-info/METADATA,sha256=Wtmg0dACPtcIDHq93LetjVlxqOerxZ1VGQdsa1ZgUds,9327
685
+ arelle_release-2.37.56.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
686
+ arelle_release-2.37.56.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
687
+ arelle_release-2.37.56.dist-info/top_level.txt,sha256=fwU7SYawL4_r-sUMRg7r1nYVGjFMSDvRWx8VGAXEw7w,7
688
+ arelle_release-2.37.56.dist-info/RECORD,,