arelle-release 2.37.47__py3-none-any.whl → 2.37.49__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.

Files changed (100) hide show
  1. arelle/CntlrCmdLine.py +5 -1
  2. arelle/ModelObjectFactory.py +18 -2
  3. arelle/_version.py +2 -2
  4. arelle/plugin/validate/EDINET/Constants.py +6 -0
  5. arelle/plugin/validate/EDINET/PluginValidationDataExtension.py +134 -7
  6. arelle/plugin/validate/EDINET/Statement.py +139 -0
  7. arelle/plugin/validate/EDINET/rules/contexts.py +5 -10
  8. arelle/plugin/validate/EDINET/rules/edinet.py +89 -51
  9. arelle/plugin/validate/EDINET/rules/gfm.py +41 -0
  10. {arelle_release-2.37.47.dist-info → arelle_release-2.37.49.dist-info}/METADATA +3 -1
  11. {arelle_release-2.37.47.dist-info → arelle_release-2.37.49.dist-info}/RECORD +15 -99
  12. arelle/archive/CustomLogger.py +0 -43
  13. arelle/archive/LoadEFMvalidate.py +0 -32
  14. arelle/archive/LoadSavePreLbCsv.py +0 -26
  15. arelle/archive/LoadValidate.cs +0 -31
  16. arelle/archive/LoadValidate.py +0 -36
  17. arelle/archive/LoadValidateCmdLine.java +0 -69
  18. arelle/archive/LoadValidatePostedZip.java +0 -57
  19. arelle/archive/LoadValidateWebService.java +0 -34
  20. arelle/archive/SaveTableToExelle.py +0 -140
  21. arelle/archive/TR3toTR4.py +0 -88
  22. arelle/archive/plugin/ESEF_2022/__init__.py +0 -47
  23. arelle/archive/plugin/bigInstance.py +0 -394
  24. arelle/archive/plugin/cmdWebServerExtension.py +0 -43
  25. arelle/archive/plugin/crashTest.py +0 -38
  26. arelle/archive/plugin/functionsXmlCreation.py +0 -106
  27. arelle/archive/plugin/hello_i18n.pot +0 -26
  28. arelle/archive/plugin/hello_i18n.py +0 -32
  29. arelle/archive/plugin/importTestChild1.py +0 -21
  30. arelle/archive/plugin/importTestChild2.py +0 -22
  31. arelle/archive/plugin/importTestGrandchild1.py +0 -21
  32. arelle/archive/plugin/importTestGrandchild2.py +0 -21
  33. arelle/archive/plugin/importTestImported1.py +0 -23
  34. arelle/archive/plugin/importTestImported11.py +0 -22
  35. arelle/archive/plugin/importTestParent.py +0 -48
  36. arelle/archive/plugin/instanceInfo.py +0 -306
  37. arelle/archive/plugin/loadFromOIM-2018.py +0 -1282
  38. arelle/archive/plugin/locale/fr/LC_MESSAGES/hello_i18n.po +0 -25
  39. arelle/archive/plugin/objectmaker.py +0 -285
  40. arelle/archive/plugin/packagedImportTest/__init__.py +0 -47
  41. arelle/archive/plugin/packagedImportTest/importTestChild1.py +0 -21
  42. arelle/archive/plugin/packagedImportTest/importTestChild2.py +0 -22
  43. arelle/archive/plugin/packagedImportTest/importTestGrandchild1.py +0 -21
  44. arelle/archive/plugin/packagedImportTest/importTestGrandchild2.py +0 -21
  45. arelle/archive/plugin/packagedImportTest/importTestImported1.py +0 -24
  46. arelle/archive/plugin/packagedImportTest/importTestImported11.py +0 -21
  47. arelle/archive/plugin/packagedImportTest/subdir/importTestImported111.py +0 -21
  48. arelle/archive/plugin/packagedImportTest/subdir/subsubdir/importTestImported1111.py +0 -21
  49. arelle/archive/plugin/sakaCalendar.py +0 -215
  50. arelle/archive/plugin/saveInstanceInfoset.py +0 -121
  51. arelle/archive/plugin/sphinx/FormulaGenerator.py +0 -823
  52. arelle/archive/plugin/sphinx/SphinxContext.py +0 -404
  53. arelle/archive/plugin/sphinx/SphinxEvaluator.py +0 -783
  54. arelle/archive/plugin/sphinx/SphinxMethods.py +0 -1287
  55. arelle/archive/plugin/sphinx/SphinxParser.py +0 -1093
  56. arelle/archive/plugin/sphinx/SphinxValidator.py +0 -163
  57. arelle/archive/plugin/sphinx/US-GAAP Ratios Example.xsr +0 -52
  58. arelle/archive/plugin/sphinx/__init__.py +0 -285
  59. arelle/archive/plugin/streamingExtensions.py +0 -335
  60. arelle/archive/plugin/updateTableLB.py +0 -242
  61. arelle/archive/plugin/validate/SBRnl/CustomLoader.py +0 -19
  62. arelle/archive/plugin/validate/SBRnl/DTS.py +0 -305
  63. arelle/archive/plugin/validate/SBRnl/Dimensions.py +0 -357
  64. arelle/archive/plugin/validate/SBRnl/Document.py +0 -799
  65. arelle/archive/plugin/validate/SBRnl/Filing.py +0 -467
  66. arelle/archive/plugin/validate/SBRnl/__init__.py +0 -75
  67. arelle/archive/plugin/validate/SBRnl/config.xml +0 -26
  68. arelle/archive/plugin/validate/SBRnl/sbr-nl-taxonomies.xml +0 -754
  69. arelle/archive/plugin/validate/USBestPractices.py +0 -570
  70. arelle/archive/plugin/validate/USCorpAction.py +0 -557
  71. arelle/archive/plugin/validate/USSecTagging.py +0 -337
  72. arelle/archive/plugin/validate/XDC/__init__.py +0 -77
  73. arelle/archive/plugin/validate/XDC/config.xml +0 -20
  74. arelle/archive/plugin/validate/XFsyntax/__init__.py +0 -64
  75. arelle/archive/plugin/validate/XFsyntax/xf.py +0 -2227
  76. arelle/archive/plugin/validate/calc2.py +0 -536
  77. arelle/archive/plugin/validateSchemaLxml.py +0 -156
  78. arelle/archive/plugin/validateTableInfoset.py +0 -52
  79. arelle/archive/us-gaap-dei-docType-extraction-frm.xml +0 -90
  80. arelle/archive/us-gaap-dei-ratio-cash-frm.xml +0 -150
  81. arelle/examples/plugin/formulaSuiteConverter.py +0 -212
  82. arelle/examples/plugin/functionsCustom.py +0 -59
  83. arelle/examples/plugin/hello_dolly.py +0 -64
  84. arelle/examples/plugin/multi.py +0 -58
  85. arelle/examples/plugin/rssSaveOim.py +0 -96
  86. arelle/examples/plugin/validate/XYZ/DisclosureSystems.py +0 -2
  87. arelle/examples/plugin/validate/XYZ/PluginValidationDataExtension.py +0 -10
  88. arelle/examples/plugin/validate/XYZ/ValidationPluginExtension.py +0 -50
  89. arelle/examples/plugin/validate/XYZ/__init__.py +0 -75
  90. arelle/examples/plugin/validate/XYZ/resources/config.xml +0 -16
  91. arelle/examples/plugin/validate/XYZ/rules/__init__.py +0 -0
  92. arelle/examples/plugin/validate/XYZ/rules/rules01.py +0 -110
  93. arelle/examples/plugin/validate/XYZ/rules/rules02.py +0 -59
  94. arelle/scripts-macOS/startWebServer.command +0 -3
  95. arelle/scripts-unix/startWebServer.sh +0 -1
  96. arelle/scripts-windows/startWebServer.bat +0 -5
  97. {arelle_release-2.37.47.dist-info → arelle_release-2.37.49.dist-info}/WHEEL +0 -0
  98. {arelle_release-2.37.47.dist-info → arelle_release-2.37.49.dist-info}/entry_points.txt +0 -0
  99. {arelle_release-2.37.47.dist-info → arelle_release-2.37.49.dist-info}/licenses/LICENSE.md +0 -0
  100. {arelle_release-2.37.47.dist-info → arelle_release-2.37.49.dist-info}/top_level.txt +0 -0
arelle/CntlrCmdLine.py CHANGED
@@ -620,7 +620,11 @@ class ParserForDynamicPlugins:
620
620
 
621
621
 
622
622
  def _pluginHasCliOptions(moduleInfo):
623
- return "CntlrCmdLine.Options" in moduleInfo["classMethods"]
623
+ if "CntlrCmdLine.Options" in moduleInfo["classMethods"]:
624
+ return True
625
+ if imports := moduleInfo.get("imports"):
626
+ return any(_pluginHasCliOptions(importedModule) for importedModule in imports)
627
+ return False
624
628
 
625
629
 
626
630
  class CntlrCmdLine(Cntlr.Cntlr):
@@ -68,6 +68,22 @@ LINKBASE = 2
68
68
  VERSIONINGREPORT = 3
69
69
  RSSFEED = 4
70
70
 
71
+ LINK_LOCALNAME_TO_MODEL_CLASS = {
72
+ 'loc': ModelLocator,
73
+ 'label': ModelResource,
74
+ 'reference': ModelResource,
75
+ 'roleType': ModelRoleType,
76
+ 'arcroleType': ModelRoleType,
77
+ } | {
78
+ q.localName: ModelObject
79
+ for q in [
80
+ XbrlConst.qnLinkCalculationArc,
81
+ XbrlConst.qnLinkDefinitionArc,
82
+ XbrlConst.qnLinkPresentationArc,
83
+ XbrlConst.qnLinkReferenceArc,
84
+ ]
85
+ }
86
+
71
87
 
72
88
  class KnownNamespacesModelObjectClassLookup(etree.CustomElementClassLookup):
73
89
  def __init__(self, modelXbrl: ModelXbrl, fallback: etree.ElementClassLookup | None = None) -> None:
@@ -106,8 +122,8 @@ class KnownNamespacesModelObjectClassLookup(etree.CustomElementClassLookup):
106
122
  elif ns == XbrlConst.link:
107
123
  if self.type is None:
108
124
  self.type = LINKBASE
109
- if ln == "roleType" or ln == "arcroleType":
110
- return ModelRoleType
125
+ if modelObjectClass := LINK_LOCALNAME_TO_MODEL_CLASS.get(ln):
126
+ return modelObjectClass
111
127
  elif ns == "http://edgar/2009/conformance":
112
128
  # don't force loading of test schema
113
129
  if ln == "variation":
arelle/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '2.37.47'
21
- __version_tuple__ = version_tuple = (2, 37, 47)
20
+ __version__ = version = '2.37.49'
21
+ __version_tuple__ = version_tuple = (2, 37, 49)
@@ -5,6 +5,11 @@ from enum import Enum
5
5
 
6
6
  from arelle.ModelValue import qname
7
7
 
8
+ class AccountingStandard(Enum):
9
+ IFRS = 'IFRS'
10
+ JAPAN_GAAP = 'Japan GAAP'
11
+ US_GAAP = 'US GAAP'
12
+
8
13
  class FormType(Enum):
9
14
  FORM_2_4 = '第二号の四様式'
10
15
  FORM_2_7 = '第二号の七様式'
@@ -22,3 +27,4 @@ qnEdinetManifestIxbrl = qname("{http://disclosure.edinet-fsa.go.jp/2013/manifest
22
27
  qnEdinetManifestList = qname("{http://disclosure.edinet-fsa.go.jp/2013/manifest}list")
23
28
  qnEdinetManifestTitle = qname("{http://disclosure.edinet-fsa.go.jp/2013/manifest}title")
24
29
  qnEdinetManifestTocComposition = qname("{http://disclosure.edinet-fsa.go.jp/2013/manifest}tocComposition")
30
+ xhtmlDtdExtension = "xhtml1-strict-ix.dtd"
@@ -3,15 +3,19 @@ See COPYRIGHT.md for copyright information.
3
3
  """
4
4
  from __future__ import annotations
5
5
 
6
- import zipfile
6
+ from collections import defaultdict
7
7
  from dataclasses import dataclass
8
+ from decimal import Decimal
8
9
  from functools import lru_cache
9
- from pathlib import Path
10
+ from operator import attrgetter
11
+ from typing import Callable, Hashable, Iterable, cast
10
12
 
11
13
  import regex
12
14
 
15
+ from arelle.LinkbaseType import LinkbaseType
13
16
  from arelle.ModelDocument import Type as ModelDocumentType
14
- from arelle.ModelInstanceObject import ModelFact
17
+ from arelle.ModelDtsObject import ModelConcept
18
+ from arelle.ModelInstanceObject import ModelFact, ModelUnit, ModelContext
15
19
  from arelle.ModelObject import ModelObject
16
20
  from arelle.ModelValue import QName, qname
17
21
  from arelle.ModelXbrl import ModelXbrl
@@ -23,18 +27,23 @@ from arelle.utils.PluginData import PluginData
23
27
  from .Constants import CORPORATE_FORMS
24
28
  from .ControllerPluginData import ControllerPluginData
25
29
  from .ManifestInstance import ManifestInstance
30
+ from .Statement import Statement, STATEMENTS, BalanceSheet, StatementInstance, StatementType
26
31
 
27
32
  _: TypeGetText
28
33
 
29
34
 
35
+ _DEBIT_QNAME_PATTERN = regex.compile('.*(Liability|Liabilities|Equity)')
36
+
37
+
30
38
  @dataclass
31
39
  class PluginValidationDataExtension(PluginData):
40
+ accountingStandardsDeiQn: QName
32
41
  assetsIfrsQn: QName
42
+ consolidatedOrNonConsolidatedAxisQn: QName
33
43
  documentTypeDeiQn: QName
34
44
  jpcrpEsrFilingDateCoverPageQn: QName
35
45
  jpcrpFilingDateCoverPageQn: QName
36
46
  jpspsFilingDateCoverPageQn: QName
37
- liabilitiesAndEquityIfrsQn: QName
38
47
  nonConsolidatedMemberQn: QName
39
48
  ratioOfFemaleDirectorsAndOtherOfficersQn: QName
40
49
 
@@ -50,12 +59,13 @@ class PluginValidationDataExtension(PluginData):
50
59
  jpigpNamespace = "http://disclosure.edinet-fsa.go.jp/taxonomy/jpigp/2024-11-01/jpigp_cor"
51
60
  jppfsNamespace = "http://disclosure.edinet-fsa.go.jp/taxonomy/jppfs/2024-11-01/jppfs_cor"
52
61
  jpspsNamespace = 'http://disclosure.edinet-fsa.go.jp/taxonomy/jpsps/2024-11-01/jpsps_cor'
62
+ self.accountingStandardsDeiQn = qname(jpdeiNamespace, 'AccountingStandardsDEI')
53
63
  self.assetsIfrsQn = qname(jpigpNamespace, 'AssetsIFRS')
64
+ self.consolidatedOrNonConsolidatedAxisQn = qname(jppfsNamespace, 'ConsolidatedOrNonConsolidatedAxis')
54
65
  self.documentTypeDeiQn = qname(jpdeiNamespace, 'DocumentTypeDEI')
55
66
  self.jpcrpEsrFilingDateCoverPageQn = qname(jpcrpEsrNamespace, 'FilingDateCoverPage')
56
67
  self.jpcrpFilingDateCoverPageQn = qname(jpcrpNamespace, 'FilingDateCoverPage')
57
68
  self.jpspsFilingDateCoverPageQn = qname(jpspsNamespace, 'FilingDateCoverPage')
58
- self.liabilitiesAndEquityIfrsQn = qname(jpigpNamespace, "LiabilitiesAndEquityIFRS")
59
69
  self.nonConsolidatedMemberQn = qname(jppfsNamespace, "NonConsolidatedMember")
60
70
  self.ratioOfFemaleDirectorsAndOtherOfficersQn = qname(jpcrpNamespace, "RatioOfFemaleDirectorsAndOtherOfficers")
61
71
 
@@ -65,6 +75,27 @@ class PluginValidationDataExtension(PluginData):
65
75
  def __hash__(self) -> int:
66
76
  return id(self)
67
77
 
78
+ @lru_cache(1)
79
+ def _contextMatchesStatement(self, modelXbrl: ModelXbrl, contextId: str, statement: Statement) -> bool:
80
+ """
81
+ :return: Whether the context's facts are applicable to the given statement.
82
+ """
83
+ if 'Interim' in contextId:
84
+ # valid06.zip suggests "interim"" contexts are not considered for balance sheets.
85
+ return False
86
+ context = modelXbrl.contexts[contextId]
87
+ if not all(dimQn == self.consolidatedOrNonConsolidatedAxisQn for dimQn in context.qnameDims):
88
+ return False
89
+ memberValue = context.dimMemberQname(self.consolidatedOrNonConsolidatedAxisQn, includeDefaults=True)
90
+ contextIsConsolidated = memberValue != self.nonConsolidatedMemberQn
91
+ return bool(statement.isConsolidated == contextIsConsolidated)
92
+
93
+ def _isDebitConcept(self, concept: ModelConcept) -> bool:
94
+ """
95
+ :return: Whether the given concept is a debit concept.
96
+ """
97
+ return bool(_DEBIT_QNAME_PATTERN.match(concept.qname.localName))
98
+
68
99
  @lru_cache(1)
69
100
  def isCorporateForm(self, modelXbrl: ModelXbrl) -> bool:
70
101
  documentTypes = self.getDocumentTypes(modelXbrl)
@@ -72,6 +103,79 @@ class PluginValidationDataExtension(PluginData):
72
103
  return True
73
104
  return False
74
105
 
106
+
107
+ def getBalanceSheets(self, modelXbrl: ModelXbrl, statement: Statement) -> list[BalanceSheet]:
108
+ """
109
+ :return: Balance sheet data for each context/unit pairing the given statement.
110
+ """
111
+ balanceSheets: list[BalanceSheet] = []
112
+ if statement.roleUri not in modelXbrl.roleTypes:
113
+ return balanceSheets
114
+ if statement.statementType not in (
115
+ StatementType.BALANCE_SHEET,
116
+ StatementType.STATEMENT_OF_FINANCIAL_POSITION
117
+ ):
118
+ return balanceSheets
119
+
120
+ relSet = modelXbrl.relationshipSet(
121
+ tuple(LinkbaseType.CALCULATION.getArcroles()),
122
+ linkrole=statement.roleUri
123
+ )
124
+ rootConcepts = relSet.rootConcepts
125
+ if len(rootConcepts) == 0:
126
+ return balanceSheets
127
+
128
+ # GFM 1.2.7 and 1.2.10 asserts no duplicate contexts and units, respectively,
129
+ # so context and unit IDs can be used as a key.
130
+ factsByContextIdAndUnitId = self.getFactsByContextAndUnit(
131
+ modelXbrl,
132
+ attrgetter("id"),
133
+ attrgetter("id"),
134
+ tuple(concept.qname for concept in rootConcepts)
135
+ )
136
+
137
+ for (contextId, unitId), facts in factsByContextIdAndUnitId.items():
138
+ if not self._contextMatchesStatement(modelXbrl, contextId, statement):
139
+ continue
140
+ assetSum = Decimal(0)
141
+ liabilitiesAndEquitySum = Decimal(0)
142
+ for fact in facts:
143
+ if isinstance(fact.xValue, float):
144
+ value = Decimal(fact.xValue)
145
+ else:
146
+ value = cast(Decimal, fact.xValue)
147
+ if self._isDebitConcept(fact.concept):
148
+ liabilitiesAndEquitySum += value
149
+ else:
150
+ assetSum += value
151
+ balanceSheets.append(
152
+ BalanceSheet(
153
+ assetsTotal=assetSum,
154
+ contextId=str(contextId),
155
+ facts=facts,
156
+ liabilitiesAndEquityTotal=liabilitiesAndEquitySum,
157
+ unitId=str(unitId),
158
+ )
159
+ )
160
+ return balanceSheets
161
+
162
+ @lru_cache(1)
163
+ def getStatementInstance(self, modelXbrl: ModelXbrl, statement: Statement) -> StatementInstance | None:
164
+ if statement.roleUri not in modelXbrl.roleTypes:
165
+ return None
166
+ return StatementInstance(
167
+ balanceSheets=self.getBalanceSheets(modelXbrl, statement),
168
+ statement=statement,
169
+ )
170
+
171
+ @lru_cache(1)
172
+ def getStatementInstances(self, modelXbrl: ModelXbrl) -> list[StatementInstance]:
173
+ return [
174
+ statementInstance
175
+ for statement in STATEMENTS
176
+ if (statementInstance := self.getStatementInstance(modelXbrl, statement)) is not None
177
+ ]
178
+
75
179
  @lru_cache(1)
76
180
  def getDeduplicatedFacts(self, modelXbrl: ModelXbrl) -> list[ModelFact]:
77
181
  return getDeduplicatedFacts(modelXbrl, DeduplicationType.CONSISTENT_PAIRS)
@@ -85,6 +189,24 @@ class PluginValidationDataExtension(PluginData):
85
189
  documentTypes.add(fact.textValue)
86
190
  return documentTypes
87
191
 
192
+ def getFactsByContextAndUnit(
193
+ self, modelXbrl: ModelXbrl,
194
+ getContextKey: Callable[[ModelContext], Hashable],
195
+ getUnitKey: Callable[[ModelUnit], Hashable],
196
+ qnames: tuple[QName, ...] | None = None,
197
+ ) -> dict[tuple[Hashable, Hashable], list[ModelFact]]:
198
+ deduplicatedFacts = self.getDeduplicatedFacts(modelXbrl)
199
+ getFactsByContextAndUnit = defaultdict(list)
200
+ for fact in deduplicatedFacts:
201
+ if qnames is not None and fact.qname not in qnames:
202
+ continue
203
+ if fact.context is None or fact.unit is None:
204
+ continue
205
+ contextKey = getContextKey(fact.context)
206
+ unitKey = getUnitKey(fact.unit)
207
+ getFactsByContextAndUnit[(contextKey, unitKey)].append(fact)
208
+ return dict(getFactsByContextAndUnit)
209
+
88
210
  @lru_cache(1)
89
211
  def getFootnoteLinkElements(self, modelXbrl: ModelXbrl) -> list[ModelObject | LinkPrototype]:
90
212
  # TODO: Consolidate with similar implementations in EDGAR and FERC
@@ -113,8 +235,13 @@ class PluginValidationDataExtension(PluginData):
113
235
  return controllerPluginData.matchManifestInstance(modelXbrl.ixdsDocUrls)
114
236
 
115
237
  def hasValidNonNilFact(self, modelXbrl: ModelXbrl, qname: QName) -> bool:
116
- requiredFacts = modelXbrl.factsByQname.get(qname, set())
117
- return any(fact.xValid >= VALID and not fact.isNil for fact in requiredFacts)
238
+ return any(fact is not None for fact in self.iterValidNonNilFacts(modelXbrl, qname))
118
239
 
119
240
  def isStandardTaxonomyUrl(self, uri: str, modelXbrl: ModelXbrl) -> bool:
120
241
  return modelXbrl.modelManager.disclosureSystem.hrefValidForDisclosureSystem(uri)
242
+
243
+ def iterValidNonNilFacts(self, modelXbrl: ModelXbrl, qname: QName) -> Iterable[ModelFact]:
244
+ facts = modelXbrl.factsByQname.get(qname, set())
245
+ for fact in facts:
246
+ if fact.xValid >= VALID and not fact.isNil:
247
+ yield fact
@@ -0,0 +1,139 @@
1
+ from dataclasses import dataclass
2
+ from decimal import Decimal
3
+ from enum import Enum
4
+
5
+ from regex import regex
6
+
7
+ from arelle.ModelInstanceObject import ModelFact
8
+
9
+ CONSOLIDATED_ROLE_URI_PATTERN = regex.compile(r'.*rol_[\w]*Consolidated')
10
+
11
+ STATEMENT_ROLE_URIS = frozenset([
12
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedQuarterPeriodConsolidatedStatementOfComprehensiveIncomeIFRS',
13
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedQuarterPeriodConsolidatedStatementOfComprehensiveIncomeSingleStatementIFRS',
14
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedQuarterPeriodConsolidatedStatementOfProfitOrLossIFRS',
15
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedQuarterPeriodStatementOfComprehensiveIncomeIFRS',
16
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedQuarterPeriodStatementOfComprehensiveIncomeSingleStatementIFRS',
17
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedQuarterPeriodStatementOfProfitOrLossIFRS',
18
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedQuarterlyConsolidatedStatementOfCashFlowsIFRS',
19
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedQuarterlyConsolidatedStatementOfChangesInEquityIFRS',
20
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedQuarterlyConsolidatedStatementOfComprehensiveIncomeIFRS',
21
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedQuarterlyConsolidatedStatementOfComprehensiveIncomeSingleStatementIFRS',
22
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedQuarterlyConsolidatedStatementOfFinancialPositionIFRS',
23
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedQuarterlyConsolidatedStatementOfProfitOrLossIFRS',
24
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedQuarterlyStatementOfCashFlowsIFRS',
25
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedQuarterlyStatementOfChangesInEquityIFRS',
26
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedQuarterlyStatementOfComprehensiveIncomeIFRS',
27
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedQuarterlyStatementOfComprehensiveIncomeSingleStatementIFRS',
28
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedQuarterlyStatementOfFinancialPositionIFRS',
29
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedQuarterlyStatementOfProfitOrLossIFRS',
30
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedSemiAnnualConsolidatedStatementOfCashFlowsIFRS',
31
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedSemiAnnualConsolidatedStatementOfChangesInEquityIFRS',
32
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedSemiAnnualConsolidatedStatementOfComprehensiveIncomeIFRS',
33
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedSemiAnnualConsolidatedStatementOfComprehensiveIncomeSingleStatementIFRS',
34
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedSemiAnnualConsolidatedStatementOfFinancialPositionIFRS',
35
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedSemiAnnualConsolidatedStatementOfProfitOrLossIFRS',
36
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedSemiAnnualStatementOfCashFlowsIFRS',
37
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedSemiAnnualStatementOfChangesInEquityIFRS',
38
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedSemiAnnualStatementOfComprehensiveIncomeIFRS',
39
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedSemiAnnualStatementOfComprehensiveIncomeSingleStatementIFRS',
40
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedSemiAnnualStatementOfFinancialPositionIFRS',
41
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedSemiAnnualStatementOfProfitOrLossIFRS',
42
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedYearToQuarterEndConsolidatedStatementOfComprehensiveIncomeIFRS',
43
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedYearToQuarterEndConsolidatedStatementOfComprehensiveIncomeSingleStatementIFRS',
44
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedYearToQuarterEndConsolidatedStatementOfProfitOrLossIFRS',
45
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedYearToQuarterEndStatementOfComprehensiveIncomeIFRS',
46
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedYearToQuarterEndStatementOfComprehensiveIncomeSingleStatementIFRS',
47
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_CondensedYearToQuarterEndStatementOfProfitOrLossIFRS',
48
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_ConsolidatedStatementOfCashFlowsIFRS',
49
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_ConsolidatedStatementOfChangesInEquityIFRS',
50
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_ConsolidatedStatementOfComprehensiveIncomeIFRS',
51
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_ConsolidatedStatementOfComprehensiveIncomeSingleStatementIFRS',
52
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_ConsolidatedStatementOfFinancialPositionIFRS',
53
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_ConsolidatedStatementOfProfitOrLossIFRS',
54
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_StatementOfCashFlowsIFRS',
55
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_StatementOfChangesInEquityIFRS',
56
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_StatementOfComprehensiveIncomeIFRS',
57
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_StatementOfComprehensiveIncomeSingleStatementIFRS',
58
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_StatementOfFinancialPositionIFRS',
59
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_StatementOfProfitOrLossIFRS',
60
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_std_ConsolidatedStatementOfCashFlowsIFRS',
61
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_std_ConsolidatedStatementOfChangesInEquityIFRS',
62
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_std_ConsolidatedStatementOfComprehensiveIncomeIFRS',
63
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_std_ConsolidatedStatementOfFinancialPositionIFRS',
64
+ 'http://disclosure.edinet-fsa.go.jp/role/jpigp/rol_std_ConsolidatedStatementOfProfitOrLossIFRS',
65
+ 'http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_BalanceSheet',
66
+ 'http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_ConsolidatedBalanceSheet',
67
+ 'http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_QuarterlyBalanceSheet',
68
+ 'http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_QuarterlyConsolidatedBalanceSheet',
69
+ 'http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_SemiAnnualBalanceSheet',
70
+ 'http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_SemiAnnualConsolidatedBalanceSheet',
71
+ 'http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_Type1SemiAnnualBalanceSheet',
72
+ 'http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_Type1SemiAnnualConsolidatedBalanceSheet',
73
+ 'http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_std_BalanceSheet',
74
+ 'http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_std_ConsolidatedBalanceSheet',
75
+ 'http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_std_QuarterlyBalanceSheet',
76
+ 'http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_std_QuarterlyConsolidatedBalanceSheet',
77
+ 'http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_std_SemiAnnualBalanceSheet',
78
+ 'http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_std_SemiAnnualConsolidatedBalanceSheet',
79
+ 'http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_std_Type1SemiAnnualBalanceSheet',
80
+ 'http://disclosure.edinet-fsa.go.jp/role/jppfs/rol_std_Type1SemiAnnualConsolidatedBalanceSheet',
81
+ ])
82
+
83
+
84
+ class StatementType(Enum):
85
+ BALANCE_SHEET = 'BalanceSheet'
86
+ CONSOLIDATED_BALANCE_SHEET = 'ConsolidatedBalanceSheetIFRS'
87
+ STATEMENT_OF_CASH_FLOWS = 'StatementOfCashFlowsIFRS'
88
+ STATEMENT_OF_CHANGES_IN_EQUITY = 'StatementOfChangesInEquityIFRS'
89
+ STATEMENT_OF_COMPREHENSIVE_INCOME = 'StatementOfComprehensiveIncomeIFRS'
90
+ STATEMENT_OF_COMPREHENSIVE_INCOME_SINGLE_STATEMENT = 'StatementOfComprehensiveIncomeSingleStatementIFRS'
91
+ STATEMENT_OF_FINANCIAL_POSITION = 'StatementOfFinancialPositionIFRS'
92
+ STATEMENT_OF_PROFIT_OR_LOSS = 'StatementOfProfitOrLossIFRS'
93
+
94
+
95
+ @dataclass(frozen=True)
96
+ class Statement:
97
+ isConsolidated: bool
98
+ roleUri: str
99
+ statementType: StatementType
100
+
101
+
102
+ @dataclass(frozen=True)
103
+ class BalanceSheet:
104
+ assetsTotal: Decimal
105
+ contextId: str
106
+ facts: list[ModelFact]
107
+ liabilitiesAndEquityTotal: Decimal
108
+ unitId: str
109
+
110
+
111
+ @dataclass(frozen=True)
112
+ class StatementInstance:
113
+ balanceSheets: list[BalanceSheet]
114
+ statement: Statement
115
+
116
+ def _buildStatements() -> frozenset[Statement]:
117
+ """
118
+ Build a frozenset of Statement objects from the STATEMENT_ROLE_URIS.
119
+ This is done to avoid re-evaluating the set comprehension multiple times.
120
+ """
121
+ statements = []
122
+ for roleUri in STATEMENT_ROLE_URIS:
123
+ isConsolidated = bool(CONSOLIDATED_ROLE_URI_PATTERN.match(roleUri))
124
+ statementType=next(
125
+ statementType
126
+ for statementType in StatementType
127
+ if roleUri.endswith(statementType.value)
128
+ )
129
+ statements.append(
130
+ Statement(
131
+ isConsolidated=isConsolidated,
132
+ roleUri=roleUri,
133
+ statementType=statementType
134
+ )
135
+ )
136
+ return frozenset(statements)
137
+
138
+
139
+ STATEMENTS = _buildStatements()
@@ -6,8 +6,6 @@ from __future__ import annotations
6
6
  from collections import defaultdict
7
7
  from typing import Any, Iterable
8
8
 
9
- import regex
10
-
11
9
  from arelle import XbrlConst
12
10
  from arelle.ModelDtsObject import ModelConcept
13
11
  from arelle.ValidateXbrl import ValidateXbrl
@@ -166,14 +164,11 @@ def rule_EC8054W(
166
164
  for context in val.modelXbrl.contexts.values():
167
165
  if pluginData.nonConsolidatedMemberQn.localName not in context.id:
168
166
  continue
169
- memberQnames = set()
170
- for scenarioElt in context.iterdescendants(XbrlConst.qnXbrlScenario.clarkNotation):
171
- for memberElt in scenarioElt.iterdescendants(
172
- XbrlConst.qnXbrldiExplicitMember.clarkNotation,
173
- XbrlConst.qnXbrldiTypedMember.clarkNotation
174
- ):
175
- memberQnames.add(memberElt.xValue)
176
- if pluginData.nonConsolidatedMemberQn not in memberQnames:
167
+ member = context.dimMemberQname(
168
+ pluginData.consolidatedOrNonConsolidatedAxisQn,
169
+ includeDefaults=True
170
+ )
171
+ if member != pluginData.nonConsolidatedMemberQn:
177
172
  yield Validation.warning(
178
173
  codes='EDINET.EC8054W',
179
174
  msg=_("For the context ID (%(contextId)s), \"NonConsolidatedMember\" "
@@ -3,9 +3,7 @@ See COPYRIGHT.md for copyright information.
3
3
  """
4
4
  from __future__ import annotations
5
5
 
6
- from collections import defaultdict
7
- from decimal import Decimal
8
- from typing import Any, Iterable, cast
6
+ from typing import Any, Iterable
9
7
 
10
8
  from arelle import XbrlConst, ValidateDuplicateFacts
11
9
  from arelle.LinkbaseType import LinkbaseType
@@ -15,12 +13,71 @@ from arelle.typing import TypeGetText
15
13
  from arelle.utils.PluginHooks import ValidationHook
16
14
  from arelle.utils.validate.Decorator import validation
17
15
  from arelle.utils.validate.Validation import Validation
16
+ from ..Constants import AccountingStandard
18
17
  from ..DisclosureSystems import (DISCLOSURE_SYSTEM_EDINET)
19
18
  from ..PluginValidationDataExtension import PluginValidationDataExtension
19
+ from ..Statement import StatementType
20
20
 
21
21
  _: TypeGetText
22
22
 
23
23
 
24
+ @validation(
25
+ hook=ValidationHook.XBRL_FINALLY,
26
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
27
+ )
28
+ def rule_balances(
29
+ pluginData: PluginValidationDataExtension,
30
+ val: ValidateXbrl,
31
+ *args: Any,
32
+ **kwargs: Any,
33
+ ) -> Iterable[Validation]:
34
+ """
35
+ EDINET.EC8057W: On the consolidated balance sheet, the sum of all liabilities and
36
+ equity must equal the sum of all assets.
37
+ EDINET.EC8058W: On the nonconsolidated balance sheet, the sum of all liabilities and
38
+ equity must equal the sum of all assets.
39
+ EDINET.EC8062W: On the consolidated statement of financial position, the sum of all liabilities and
40
+ equity must equal the sum of all assets.
41
+ EDINET.EC8064W: On the nonconsolidated statement of financial position, the sum of all liabilities and
42
+ equity must equal the sum of all assets.
43
+ """
44
+ for statementInstance in pluginData.getStatementInstances(val.modelXbrl):
45
+ statement = statementInstance.statement
46
+ for balanceSheet in statementInstance.balanceSheets:
47
+ if balanceSheet.assetsTotal == balanceSheet.liabilitiesAndEquityTotal:
48
+ continue
49
+ code = None
50
+ if statement.statementType == StatementType.BALANCE_SHEET:
51
+ if statement.isConsolidated:
52
+ code = "EDINET.EC8057W"
53
+ else:
54
+ code = "EDINET.EC8058W"
55
+ elif statement.statementType == StatementType.STATEMENT_OF_FINANCIAL_POSITION:
56
+ if statement.isConsolidated:
57
+ code = "EDINET.EC8062W"
58
+ else:
59
+ code = "EDINET.EC8064W"
60
+ assert code is not None, "Unknown balance sheet encountered."
61
+ yield Validation.warning(
62
+ codes=code,
63
+ msg=_("The %(consolidated)s %(balanceSheet)s is not balanced. "
64
+ "The sum of all liabilities and equity must equal the sum of all assets. "
65
+ "Please correct the debit (%(liabilitiesAndEquitySum)s) and credit (%(assetSum)s) "
66
+ "values so that they match "
67
+ "<roleUri=%(roleUri)s> <contextID=%(contextId)s> <unitID=%(unitId)s>."),
68
+ consolidated=_("consolidated") if statement.isConsolidated
69
+ else _("nonconsolidated"),
70
+ balanceSheet=_("balance sheet") if statement.statementType == StatementType.BALANCE_SHEET
71
+ else _("statement of financial position"),
72
+ liabilitiesAndEquitySum=f"{balanceSheet.liabilitiesAndEquityTotal:,}",
73
+ assetSum=f"{balanceSheet.assetsTotal:,}",
74
+ roleUri=statement.roleUri,
75
+ contextId=balanceSheet.contextId,
76
+ unitId=balanceSheet.unitId,
77
+ modelObject=balanceSheet.facts,
78
+ )
79
+
80
+
24
81
  @validation(
25
82
  hook=ValidationHook.XBRL_FINALLY,
26
83
  disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
@@ -90,6 +147,35 @@ def rule_EC5002E(
90
147
  )
91
148
 
92
149
 
150
+ @validation(
151
+ hook=ValidationHook.XBRL_FINALLY,
152
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
153
+ )
154
+ def rule_EC5613W(
155
+ pluginData: PluginValidationDataExtension,
156
+ val: ValidateXbrl,
157
+ *args: Any,
158
+ **kwargs: Any,
159
+ ) -> Iterable[Validation]:
160
+ """
161
+ EDINET.EC5613W: Please set the DEI "Accounting Standard" value to one
162
+ of the following: "Japan GAAP", "US GAAP", "IFRS".
163
+ """
164
+ validAccountingStandards = {s.value for s in AccountingStandard}
165
+ errorFacts = [
166
+ fact for fact in pluginData.iterValidNonNilFacts(val.modelXbrl, pluginData.accountingStandardsDeiQn)
167
+ if fact.xValue not in validAccountingStandards
168
+ ]
169
+ if len(errorFacts) > 0:
170
+ yield Validation.warning(
171
+ codes='EDINET.EC5613W',
172
+ msg=_("Please set the DEI \"Accounting Standard\" value to one "
173
+ "of the following: %(values)s."),
174
+ values=', '.join(f'"{s.value}"' for s in AccountingStandard),
175
+ modelObject=errorFacts,
176
+ )
177
+
178
+
93
179
  @validation(
94
180
  hook=ValidationHook.XBRL_FINALLY,
95
181
  disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
@@ -175,54 +261,6 @@ def rule_EC8027W(
175
261
  )
176
262
 
177
263
 
178
- @validation(
179
- hook=ValidationHook.XBRL_FINALLY,
180
- disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
181
- )
182
- def rule_EC8062W(
183
- pluginData: PluginValidationDataExtension,
184
- val: ValidateXbrl,
185
- *args: Any,
186
- **kwargs: Any,
187
- ) -> Iterable[Validation]:
188
- """
189
- EDINET.EC8062W: The sum of all liabilities and equity must equal the sum of all assets.
190
- """
191
- deduplicatedFacts = pluginData.getDeduplicatedFacts(val.modelXbrl)
192
-
193
- factsByContextId = defaultdict(list)
194
- for fact in deduplicatedFacts:
195
- if fact.qname not in (pluginData.assetsIfrsQn, pluginData.liabilitiesAndEquityIfrsQn):
196
- continue
197
- if fact.contextID is None or not pluginData.contextIdPattern.fullmatch(fact.contextID):
198
- continue
199
- factsByContextId[fact.contextID].append(fact)
200
-
201
- for facts in factsByContextId.values():
202
- assetSum = Decimal(0)
203
- liabilitiesAndEquitySum = Decimal(0)
204
- for fact in facts:
205
- if isinstance(fact.xValue, float):
206
- value = Decimal(fact.xValue)
207
- else:
208
- value = cast(Decimal, fact.xValue)
209
- if fact.qname == pluginData.assetsIfrsQn:
210
- assetSum += value
211
- elif fact.qname == pluginData.liabilitiesAndEquityIfrsQn:
212
- liabilitiesAndEquitySum += value
213
- if assetSum != liabilitiesAndEquitySum:
214
- yield Validation.warning(
215
- codes='EDINET.EC8062W',
216
- msg=_("The consolidated statement of financial position is not reconciled. "
217
- "The sum of all liabilities and equity must equal the sum of all assets. "
218
- "Please correct the debit (%(liabilitiesAndEquitySum)s) and credit (%(assetSum)s) "
219
- "values so that they match."),
220
- liabilitiesAndEquitySum=f"{liabilitiesAndEquitySum:,}",
221
- assetSum=f"{assetSum:,}",
222
- modelObject=facts,
223
- )
224
-
225
-
226
264
  @validation(
227
265
  hook=ValidationHook.XBRL_FINALLY,
228
266
  disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],