arelle-release 2.37.48__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.

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.48'
21
- __version_tuple__ = version_tuple = (2, 37, 48)
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],
@@ -3,8 +3,10 @@ See COPYRIGHT.md for copyright information.
3
3
  """
4
4
  from __future__ import annotations
5
5
 
6
+ import os
6
7
  from collections import defaultdict
7
8
  from datetime import timedelta
9
+ from lxml.etree import XML, DTD
8
10
  from typing import Any, cast, Iterable
9
11
 
10
12
  import regex
@@ -28,6 +30,7 @@ from arelle.utils.validate.Validation import Validation
28
30
  from arelle.utils.validate.ValidationUtil import etreeIterWithDepth
29
31
  from ..DisclosureSystems import (DISCLOSURE_SYSTEM_EDINET)
30
32
  from ..PluginValidationDataExtension import PluginValidationDataExtension
33
+ from ..Constants import xhtmlDtdExtension
31
34
 
32
35
  _: TypeGetText
33
36
 
@@ -349,6 +352,44 @@ def rule_gfm_1_2_13(
349
352
  )
350
353
 
351
354
 
355
+ @validation(
356
+ hook=ValidationHook.XBRL_FINALLY,
357
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
358
+ )
359
+ def rule_gfm_1_2_14(
360
+ pluginData: PluginValidationDataExtension,
361
+ val: ValidateXbrl,
362
+ *args: Any,
363
+ **kwargs: Any,
364
+ ) -> Iterable[Validation]:
365
+ """
366
+ EDINET.EC5700W: [GFM.1.2.14] The content of an element with a data type of nonnum:textBlockItemType is not well-formed XML
367
+ (a format that conforms to XML grammar, such as all start and end tags being paired, and the end tag of a nested tag not coming after the end tag of its parent tag, etc.).
368
+ Please modify it so that it is well-formed.
369
+ """
370
+ CDATApattern = regex.compile(r"<!\[CDATA\[(.+)\]\]")
371
+ dtd = DTD(os.path.join(val.modelXbrl.modelManager.cntlr.configDir, xhtmlDtdExtension))
372
+ htmlBodyTemplate = "<body><div>\n{0}\n</div></body>\n"
373
+ namedEntityPattern = regex.compile("&[_A-Za-z\xC0-\xD6\xD8-\xF6\xF8-\xFF\u0100-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]"
374
+ r"[_\-\.:"
375
+ "\xB7A-Za-z0-9\xC0-\xD6\xD8-\xF6\xF8-\xFF\u0100-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u0300-\u036F\u203F-\u2040]*;")
376
+ XMLpattern = regex.compile(r".*(<|&lt;|&#x3C;|&#60;)[A-Za-z_]+[A-Za-z0-9_:]*[^>]*(/>|>|&gt;|/&gt;).*", regex.DOTALL)
377
+
378
+ for fact in val.modelXbrl.facts:
379
+ concept = fact.concept
380
+ if not fact.isNil and concept is not None and concept.isTextBlock and XMLpattern.match(fact.value):
381
+ for xmlText in [fact.value] + CDATApattern.findall(fact.value):
382
+ xmlBodyWithoutEntities = htmlBodyTemplate.format(namedEntityPattern.sub("", xmlText).replace('&','&amp;'))
383
+ textblockXml = XML(xmlBodyWithoutEntities)
384
+ if not dtd.validate(textblockXml):
385
+ yield Validation.warning(
386
+ codes='EDINET.EC5700W.GFM.1.2.14',
387
+ msg=_('The content of an element with a data type of nonnum:textBlockItemType is not well-formed XML (a format that conforms to XML grammar, '
388
+ 'such as all start and end tags being in pairs, and the end tag of a nested tag not coming after the end tag of its parent tag). '
389
+ 'Correct the content so that it is well-formed.'),
390
+ modelObject = fact
391
+ )
392
+
352
393
 
353
394
  @validation(
354
395
  hook=ValidationHook.XBRL_FINALLY,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arelle-release
3
- Version: 2.37.48
3
+ Version: 2.37.49
4
4
  Summary: An open source XBRL platform.
5
5
  Author-email: "arelle.org" <support@arelle.org>
6
6
  License-Expression: Apache-2.0
@@ -50,7 +50,9 @@ Requires-Dist: PyMySQL==1.*; extra == "db"
50
50
  Requires-Dist: pyodbc<6,>=4; extra == "db"
51
51
  Requires-Dist: rdflib<8,>=5; extra == "db"
52
52
  Provides-Extra: efm
53
+ Requires-Dist: aniso8601==10.*; extra == "efm"
53
54
  Requires-Dist: holidays==0.*; extra == "efm"
55
+ Requires-Dist: Matplotlib==3.*; extra == "efm"
54
56
  Requires-Dist: pytz; extra == "efm"
55
57
  Provides-Extra: esef
56
58
  Requires-Dist: tinycss2==1.*; extra == "esef"
@@ -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=a1G6MaRLHqusiCjZ8eE7tsseLwVuXoBxcetdjd5YD4I,515
128
+ arelle/_version.py,sha256=Ee0guntzPFkQ3dVGjmJhql2UxZBI6xicDkHrMXRgOlk,515
129
129
  arelle/typing.py,sha256=PRe-Fxwr2SBqYYUVPCJ3E7ddDX0_oOISNdT5Q97EbRM,1246
130
130
  arelle/api/Session.py,sha256=27HVuK3Bz1_21l4_RLn1IQg6v0MNsUEYrHajymyWwxI,7429
131
131
  arelle/config/creationSoftwareNames.json,sha256=5MK7XUjfDJ9OpRCCHXeOErJ1SlTBZji4WEcEOdOacx0,3128
@@ -312,22 +312,23 @@ 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=ENqevcx-d8ZMBbboxbbBnQpbyY-rzHX7_1SSuAaw75s,988
315
+ arelle/plugin/validate/EDINET/Constants.py,sha256=QgNO3ZcTfJPMIGogS4Xuyz2BtISJ_PxYP2GmNbshx8c,1136
316
316
  arelle/plugin/validate/EDINET/ControllerPluginData.py,sha256=PrEhllue_lh-sLeUy9w8I9XLMLgpo5eW-c7ZKgNyGLE,6256
317
317
  arelle/plugin/validate/EDINET/DisclosureSystems.py,sha256=3rKG42Eg-17Xx_KXU_V5yHW6I3LTwQunvf4a44C9k_4,36
318
318
  arelle/plugin/validate/EDINET/InstanceType.py,sha256=aLKb4-AJ6nDZKMOLCp7u08E9VD64ExeZy9_oGth-LTk,3207
319
319
  arelle/plugin/validate/EDINET/ManifestInstance.py,sha256=SkQV-aOsYn3CTgCkH4IXNdM3QKoiz8okwb29ftMtV3Q,6882
320
- arelle/plugin/validate/EDINET/PluginValidationDataExtension.py,sha256=A_iJox9SEWCsSWwhlno6I3VUysdAhFrtsMSNO9CcJ7c,5508
320
+ arelle/plugin/validate/EDINET/PluginValidationDataExtension.py,sha256=3EhtXN_7q3SBgqiHNzuGwlEe-yT4x1hL1t1jtZG9cYE,10983
321
+ arelle/plugin/validate/EDINET/Statement.py,sha256=0Mw5IB7LMtvUZ-2xKZfxmq67xF_dCgJo3eNLweLFRHU,9350
321
322
  arelle/plugin/validate/EDINET/UploadContents.py,sha256=L0u5171cLBKX7NT-_szRqOfkiy4Gv1xPsfpPVgPhtu0,409
322
323
  arelle/plugin/validate/EDINET/ValidationPluginExtension.py,sha256=oMY0ntLr1qIh3uMi1W_M-bT5bhXPDx048X3oDFP5zOY,2042
323
324
  arelle/plugin/validate/EDINET/__init__.py,sha256=OZ7gMknCHd0M-9nt8UOmjEZW50YQzbvSLongr9O7Yi0,3022
324
325
  arelle/plugin/validate/EDINET/resources/config.xml,sha256=7uT4GcRgk5veMLpFhPPQJxbGKiQvM52P8EMrjn0qd0g,646
325
326
  arelle/plugin/validate/EDINET/resources/edinet-taxonomies.xml,sha256=997I3RGTLg5OY3vn5hQxVFAAxOmDSOYpuyQe6VnWSY0,16285
326
327
  arelle/plugin/validate/EDINET/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
327
- arelle/plugin/validate/EDINET/rules/contexts.py,sha256=feNqSFhfOxPeV0jpSdmwULtY-__eimHtclVKtCyrLZg,7715
328
- arelle/plugin/validate/EDINET/rules/edinet.py,sha256=2PP47pJDz1jgZ4FcWS6fcBU6NIL2Paj37tBjy17FoKk,9827
328
+ arelle/plugin/validate/EDINET/rules/contexts.py,sha256=anmkmrrb8kSZRYhrgkh1dlZC1rpCgCtJqZ2TnsBMP5s,7458
329
+ arelle/plugin/validate/EDINET/rules/edinet.py,sha256=pgTmG32uEG8dDPVaBIYE_wN-hKV5AFZL0_wB11L0xHQ,11733
329
330
  arelle/plugin/validate/EDINET/rules/frta.py,sha256=N0YglHYZuLD2IuwE26viR2ViwUYjneBuMFU9vlrS0aQ,7616
330
- arelle/plugin/validate/EDINET/rules/gfm.py,sha256=4EKMho6eX-Ygl8yMBVabHQpbC-wxMvi067ubN9mp27U,21982
331
+ arelle/plugin/validate/EDINET/rules/gfm.py,sha256=udaGzYmKiCudXGE7yA_ej-Tcs3JDaUxHbR87ZITUPLA,24568
331
332
  arelle/plugin/validate/EDINET/rules/manifests.py,sha256=MoT9R_a4BzuYdQVbF7RC5wz134Ve68svSdJ3NlpO_AU,4026
332
333
  arelle/plugin/validate/EDINET/rules/upload.py,sha256=k1o12K_vMN2N5bAXPxLRwyKjghoOGrgfLijE_j_5ilQ,19811
333
334
  arelle/plugin/validate/ESEF/Const.py,sha256=JujF_XV-_TNsxjGbF-8SQS4OOZIcJ8zhCMnr-C1O5Ho,22660
@@ -679,9 +680,9 @@ arelle/utils/validate/ValidationUtil.py,sha256=9vmSvShn-EdQy56dfesyV8JjSRVPj7txr
679
680
  arelle/utils/validate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
680
681
  arelle/webserver/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
681
682
  arelle/webserver/bottle.py,sha256=P-JECd9MCTNcxCnKoDUvGcoi03ezYVOgoWgv2_uH-6M,362
682
- arelle_release-2.37.48.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
683
- arelle_release-2.37.48.dist-info/METADATA,sha256=g59sIoeKn9AoTiJUVtmrHqMiQSsGEoeLDfnAvEAASwA,9137
684
- arelle_release-2.37.48.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
685
- arelle_release-2.37.48.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
686
- arelle_release-2.37.48.dist-info/top_level.txt,sha256=fwU7SYawL4_r-sUMRg7r1nYVGjFMSDvRWx8VGAXEw7w,7
687
- arelle_release-2.37.48.dist-info/RECORD,,
683
+ arelle_release-2.37.49.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
684
+ arelle_release-2.37.49.dist-info/METADATA,sha256=l7IqnpMRntF6T7qKQ6jhMMGggWKen61_a-bAVs7PI6Y,9231
685
+ arelle_release-2.37.49.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
686
+ arelle_release-2.37.49.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
687
+ arelle_release-2.37.49.dist-info/top_level.txt,sha256=fwU7SYawL4_r-sUMRg7r1nYVGjFMSDvRWx8VGAXEw7w,7
688
+ arelle_release-2.37.49.dist-info/RECORD,,