arelle-release 2.37.62__py3-none-any.whl → 2.37.64__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/CntlrCmdLine.py +3 -0
- arelle/DisclosureSystem.py +2 -0
- arelle/FileSource.py +4 -1
- arelle/_version.py +2 -2
- arelle/api/Session.py +6 -0
- arelle/config/disclosuresystems.xsd +1 -0
- arelle/plugin/validate/EDINET/Constants.py +5 -1
- arelle/plugin/validate/EDINET/ContextRequirement.py +58 -0
- arelle/plugin/validate/EDINET/ControllerPluginData.py +69 -3
- arelle/plugin/validate/EDINET/PluginValidationDataExtension.py +1 -1
- arelle/plugin/validate/EDINET/__init__.py +10 -0
- arelle/plugin/validate/EDINET/resources/config.xml +2 -1
- arelle/plugin/validate/EDINET/rules/contexts.py +205 -2
- arelle/plugin/validate/EDINET/rules/frta.py +2 -2
- arelle/plugin/validate/EDINET/rules/gfm.py +71 -0
- arelle/plugin/validate/EDINET/rules/upload.py +2 -0
- {arelle_release-2.37.62.dist-info → arelle_release-2.37.64.dist-info}/METADATA +1 -1
- {arelle_release-2.37.62.dist-info → arelle_release-2.37.64.dist-info}/RECORD +22 -21
- {arelle_release-2.37.62.dist-info → arelle_release-2.37.64.dist-info}/WHEEL +0 -0
- {arelle_release-2.37.62.dist-info → arelle_release-2.37.64.dist-info}/entry_points.txt +0 -0
- {arelle_release-2.37.62.dist-info → arelle_release-2.37.64.dist-info}/licenses/LICENSE.md +0 -0
- {arelle_release-2.37.62.dist-info → arelle_release-2.37.64.dist-info}/top_level.txt +0 -0
arelle/CntlrCmdLine.py
CHANGED
|
@@ -826,6 +826,9 @@ class CntlrCmdLine(Cntlr.Cntlr):
|
|
|
826
826
|
else:
|
|
827
827
|
self.modelManager.disclosureSystem.select(None) # just load ordinary mappings
|
|
828
828
|
self.modelManager.validateDisclosureSystem = False
|
|
829
|
+
if self.modelManager.disclosureSystem.keepOpen:
|
|
830
|
+
# Force keepOpen if specified by disclosure system.
|
|
831
|
+
options.keepOpen = True
|
|
829
832
|
if options.baseTaxonomyValidationMode is not None:
|
|
830
833
|
self.modelManager.baseTaxonomyValidationMode = ValidateBaseTaxonomiesMode.fromName(options.baseTaxonomyValidationMode)
|
|
831
834
|
self.modelManager.validateXmlOim = bool(options.validateXmlOim)
|
arelle/DisclosureSystem.py
CHANGED
|
@@ -80,6 +80,7 @@ class DisclosureSystem:
|
|
|
80
80
|
self.utrUrl = ["http://www.xbrl.org/utr/utr.xml"]
|
|
81
81
|
self.utrStatusFilters = None
|
|
82
82
|
self.utrTypeEntries = None
|
|
83
|
+
self.keepOpen: bool = False
|
|
83
84
|
self.identifierSchemePattern = None
|
|
84
85
|
self.identifierValuePattern = None
|
|
85
86
|
self.identifierValueName = None
|
|
@@ -214,6 +215,7 @@ class DisclosureSystem:
|
|
|
214
215
|
self.utrUrl = [self.modelManager.cntlr.webCache.normalizeUrl(u, url)
|
|
215
216
|
for u in dsElt.get("utrUrl").split()]
|
|
216
217
|
self.utrStatusFilters = dsElt.get("utrStatusFilters")
|
|
218
|
+
self.keepOpen = dsElt.get("keepOpen") == "true"
|
|
217
219
|
self.identifierSchemePattern = compileAttrPattern(dsElt,"identifierSchemePattern")
|
|
218
220
|
self.identifierValuePattern = compileAttrPattern(dsElt,"identifierValuePattern")
|
|
219
221
|
self.identifierValueName = dsElt.get("identifierValueName")
|
arelle/FileSource.py
CHANGED
|
@@ -51,7 +51,10 @@ def openFileSource(
|
|
|
51
51
|
sourceFileSource: FileSource | None = None,
|
|
52
52
|
) -> FileSource:
|
|
53
53
|
if sourceZipStream:
|
|
54
|
-
if
|
|
54
|
+
if name := getattr(sourceZipStream, "name", None):
|
|
55
|
+
# Python IO convention is to use the name attribute
|
|
56
|
+
sourceZipStreamFileName: str = os.sep + str(name)
|
|
57
|
+
elif isinstance(sourceZipStream, FileNamedBytesIO) and sourceZipStream.fileName:
|
|
55
58
|
sourceZipStreamFileName = os.sep + sourceZipStream.fileName
|
|
56
59
|
else:
|
|
57
60
|
sourceZipStreamFileName = os.sep + "POSTupload.zip"
|
arelle/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '2.37.
|
|
32
|
-
__version_tuple__ = version_tuple = (2, 37,
|
|
31
|
+
__version__ = version = '2.37.64'
|
|
32
|
+
__version_tuple__ = version_tuple = (2, 37, 64)
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
arelle/api/Session.py
CHANGED
|
@@ -113,6 +113,12 @@ class Session:
|
|
|
113
113
|
) -> bool:
|
|
114
114
|
"""
|
|
115
115
|
Perform a run using the given options.
|
|
116
|
+
When using a sourceZipStream, the name attribute on it will be checked
|
|
117
|
+
and, if present, assumed to be the effective name of the zip file the
|
|
118
|
+
stream represents. When validating '.xbri' files, via a sourceZipStream,
|
|
119
|
+
it is important to set this name attribute to avoid
|
|
120
|
+
rpe:documentTypeFileExtensionMismatch errors.
|
|
121
|
+
|
|
116
122
|
:param options: Options to use for the run.
|
|
117
123
|
:param sourceZipStream: Optional stream to read source data from.
|
|
118
124
|
:param responseZipStream: Options stream to write response data to.
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
<xs:attribute name="deiFilerNameElement" use="optional" type="xs:NCName"/>
|
|
52
52
|
<xs:attribute name="deiNamespacePattern" use="optional"/>
|
|
53
53
|
<xs:attribute name="description" />
|
|
54
|
+
<xs:attribute name="keepOpen" type="xs:boolean"/>
|
|
54
55
|
<xs:attribute name="identifierSchemePattern" use="optional"/>
|
|
55
56
|
<xs:attribute name="identifierValueName" use="optional"/>
|
|
56
57
|
<xs:attribute name="identifierValuePattern" use="optional"/>
|
|
@@ -159,7 +159,7 @@ REPORT_IXBRL_FILENAME_PATTERN = regex.compile(rf'{PATTERN_MAIN}_{PATTERN_NAME}_{
|
|
|
159
159
|
AUDIT_IXBRL_FILENAME_PATTERN = regex.compile(rf'{PATTERN_AUDIT_REPORT_PREFIX}-{PATTERN_SUFFIX}_ixbrl\.htm')
|
|
160
160
|
|
|
161
161
|
PATTERN_CONTEXT_RELATIVE_PERIOD = r'(?P<relative_period>Prior[1-9]Year|CurrentYear|Prior[1-9]Interim|Interim)'
|
|
162
|
-
PATTERN_CONTEXT_PERIOD = r'(?P<relative_period>Prior[1-9]Year|CurrentYear|Prior[1-9]Interim|Interim|FilingDate|RecordDate)'
|
|
162
|
+
PATTERN_CONTEXT_PERIOD = r'(?P<relative_period>Prior[1-9]Year|CurrentYear|Prior[1-9]Interim|Prior[1-9]Quarter|Prior[1-9]YTD|Interim|FilingDate|RecordDate|CurrentQuarter|CurrentYTD)'
|
|
163
163
|
PATTERN_CONTEXT_DURATION = r'(?P<duration>Duration|Instant)'
|
|
164
164
|
PATTERN_CONTEXT_MEMBERS = r'(?P<context_members>(?:_[a-zA-Z][a-zA-Z0-9-]+)+)*'
|
|
165
165
|
PATTERN_CONTEXT_NUMBER = r'(?P<context_number>[0-9]{3})'
|
|
@@ -172,5 +172,9 @@ CONTEXT_ID_PATTERN = regex.compile(rf'{PATTERN_CONTEXT_PERIOD}{PATTERN_CONTEXT_D
|
|
|
172
172
|
# Example: Prior2YearDuration
|
|
173
173
|
FINANCIAL_STATEMENT_CONTEXT_ID_PATTERN = regex.compile(rf'^{PATTERN_CONTEXT_RELATIVE_PERIOD}{PATTERN_CONTEXT_DURATION}.*')
|
|
174
174
|
|
|
175
|
+
# Context IDs for facts associated with individual (non-consolidated) Financial Statements
|
|
176
|
+
# Example: Prior2YearDuration_NonConsolidatedMember
|
|
177
|
+
INDIVIDUAL_CONTEXT_ID_PATTERN = regex.compile(rf'^{PATTERN_CONTEXT_RELATIVE_PERIOD}{PATTERN_CONTEXT_DURATION}_NonConsolidatedMember.*')
|
|
178
|
+
|
|
175
179
|
# Accepted language codes for Japan
|
|
176
180
|
JAPAN_LANGUAGE_CODES = frozenset({'ja', 'jp', 'ja-jp', 'JA', 'JP', 'JA-JP'})
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""
|
|
2
|
+
See COPYRIGHT.md for copyright information.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Literal
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True)
|
|
11
|
+
class ContextRequirement:
|
|
12
|
+
"""
|
|
13
|
+
If {elementExists} is set, and {elementDoesNotExist} is not set,
|
|
14
|
+
any context with ID starting with {contextId} must have {element} value
|
|
15
|
+
matching the {elementMatch} DEI value (adjusted by {dayAdjustment} days).
|
|
16
|
+
"""
|
|
17
|
+
contextId: str
|
|
18
|
+
element: Literal['endDate', 'instant', 'startDate']
|
|
19
|
+
elementExists: str | None
|
|
20
|
+
elementDoesNotExist: str | None
|
|
21
|
+
elementMatch: str
|
|
22
|
+
dayAdjustment: int = 0 # days to adjust by (e.g. -1 for "day before")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# See section 3-4-2 in the Validation Guidelines.
|
|
26
|
+
CONTEXT_REQUIREMENTS = [
|
|
27
|
+
ContextRequirement('CurrentYearInstant', 'instant', None, None, 'CurrentFiscalYearEndDateDEI'),
|
|
28
|
+
ContextRequirement('Prior1YearInstant', 'instant', 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI', None, 'CurrentFiscalYearEndDateDEI'),
|
|
29
|
+
ContextRequirement('Prior1YearInstant', 'instant', None, 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI', 'PreviousFiscalYearEndDateDEI'),
|
|
30
|
+
ContextRequirement('Prior2YearInstant', 'instant', 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI', None, 'PreviousFiscalYearEndDateDEI'),
|
|
31
|
+
ContextRequirement('Prior2YearInstant', 'instant', None, 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI', 'PreviousFiscalYearStartDateDEI', -1),
|
|
32
|
+
ContextRequirement('CurrentQuarterInstant', 'instant', 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI', None, 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI'),
|
|
33
|
+
ContextRequirement('CurrentQuarterInstant', 'instant', None, 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI', 'CurrentPeriodEndDateDEI'),
|
|
34
|
+
ContextRequirement('Prior1QuarterInstant', 'instant', None, 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI', 'ComparativePeriodEndDateDEI'),
|
|
35
|
+
ContextRequirement('InterimInstant', 'instant', 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI', None, 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI'),
|
|
36
|
+
ContextRequirement('InterimInstant', 'instant', None, 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI', 'CurrentPeriodEndDateDEI'),
|
|
37
|
+
ContextRequirement('Prior1InterimInstant', 'instant', None, 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI', 'ComparativePeriodEndDateDEI'),
|
|
38
|
+
ContextRequirement('CurrentYearDuration', 'startDate', None, None, 'CurrentFiscalYearStartDateDEI'),
|
|
39
|
+
ContextRequirement('Prior1YearDuration', 'startDate', 'NextFiscalYearStartDateDEI', None, 'CurrentFiscalYearStartDateDEI'),
|
|
40
|
+
ContextRequirement('Prior1YearDuration', 'startDate', None, 'NextFiscalYearStartDateDEI', 'PreviousFiscalYearStartDateDEI'),
|
|
41
|
+
ContextRequirement('Prior2YearDuration', 'startDate', 'NextFiscalYearStartDateDEI', None, 'PreviousFiscalYearStartDateDEI'),
|
|
42
|
+
ContextRequirement('CurrentYTDDuration', 'startDate', 'NextFiscalYearStartDateDEI', None, 'NextFiscalYearStartDateDEI'),
|
|
43
|
+
ContextRequirement('CurrentYTDDuration', 'startDate', None, 'NextFiscalYearStartDateDEI', 'CurrentFiscalYearStartDateDEI'),
|
|
44
|
+
ContextRequirement('Prior1YTDDuration', 'startDate', None, 'NextFiscalYearStartDateDEI', 'PreviousFiscalYearStartDateDEI'),
|
|
45
|
+
ContextRequirement('InterimDuration', 'startDate', 'NextFiscalYearStartDateDEI', None, 'NextFiscalYearStartDateDEI'),
|
|
46
|
+
ContextRequirement('InterimDuration', 'startDate', None, 'NextFiscalYearStartDateDEI', 'CurrentFiscalYearStartDateDEI'),
|
|
47
|
+
ContextRequirement('Prior1InterimDuration', 'startDate', None, 'NextFiscalYearStartDateDEI', 'PreviousFiscalYearStartDateDEI'),
|
|
48
|
+
ContextRequirement('CurrentYearDuration', 'endDate', None, None, 'CurrentFiscalYearEndDateDEI'),
|
|
49
|
+
ContextRequirement('Prior1YearDuration', 'endDate', 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI', None, 'CurrentFiscalYearEndDateDEI'),
|
|
50
|
+
ContextRequirement('Prior1YearDuration', 'endDate', None, 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI', 'PreviousFiscalYearEndDateDEI'),
|
|
51
|
+
ContextRequirement('Prior2YearDuration', 'endDate', 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI', None, 'PreviousFiscalYearEndDateDEI'),
|
|
52
|
+
ContextRequirement('CurrentYTDDuration', 'endDate', 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI', None, 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI'),
|
|
53
|
+
ContextRequirement('CurrentYTDDuration', 'endDate', None, 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI', 'CurrentPeriodEndDateDEI'),
|
|
54
|
+
ContextRequirement('Prior1YTDDuration', 'endDate', None, 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI', 'ComparativePeriodEndDateDEI'),
|
|
55
|
+
ContextRequirement('InterimDuration', 'endDate', 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI', None, 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI'),
|
|
56
|
+
ContextRequirement('InterimDuration', 'endDate', None, 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI', 'CurrentPeriodEndDateDEI'),
|
|
57
|
+
ContextRequirement('Prior1InterimDuration', 'endDate', None, 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI', 'ComparativePeriodEndDateDEI'),
|
|
58
|
+
]
|
|
@@ -12,12 +12,14 @@ from typing import TYPE_CHECKING
|
|
|
12
12
|
|
|
13
13
|
from arelle.Cntlr import Cntlr
|
|
14
14
|
from arelle.FileSource import FileSource
|
|
15
|
-
from arelle.ModelValue import QName
|
|
15
|
+
from arelle.ModelValue import QName, TypeXValue
|
|
16
|
+
from arelle.ModelXbrl import ModelXbrl
|
|
17
|
+
from arelle.XmlValidateConst import VALID
|
|
16
18
|
from arelle.typing import TypeGetText
|
|
17
19
|
from arelle.utils.PluginData import PluginData
|
|
18
20
|
from . import Constants
|
|
19
21
|
from .CoverItemRequirements import CoverItemRequirements
|
|
20
|
-
from .DeiRequirements import DeiRequirements
|
|
22
|
+
from .DeiRequirements import DeiRequirements, DEI_LOCAL_NAMES
|
|
21
23
|
from .FilingFormat import FilingFormat
|
|
22
24
|
from .ReportFolderType import ReportFolderType
|
|
23
25
|
from .TableOfContentsBuilder import TableOfContentsBuilder
|
|
@@ -31,6 +33,8 @@ _: TypeGetText
|
|
|
31
33
|
|
|
32
34
|
@dataclass
|
|
33
35
|
class ControllerPluginData(PluginData):
|
|
36
|
+
_deiValues: dict[str, TypeXValue]
|
|
37
|
+
_loadedModelXbrls: list[ModelXbrl]
|
|
34
38
|
_manifestInstancesById: dict[str, ManifestInstance]
|
|
35
39
|
_tocBuilder: TableOfContentsBuilder
|
|
36
40
|
_uploadContents: UploadContents | None
|
|
@@ -38,6 +42,8 @@ class ControllerPluginData(PluginData):
|
|
|
38
42
|
|
|
39
43
|
def __init__(self, name: str):
|
|
40
44
|
super().__init__(name)
|
|
45
|
+
self._deiValues = {}
|
|
46
|
+
self._loadedModelXbrls = []
|
|
41
47
|
self._manifestInstancesById = {}
|
|
42
48
|
self._tocBuilder = TableOfContentsBuilder()
|
|
43
49
|
self._usedFilepaths = set()
|
|
@@ -52,6 +58,10 @@ class ControllerPluginData(PluginData):
|
|
|
52
58
|
"""
|
|
53
59
|
self._manifestInstancesById[manifestInstance.id] = manifestInstance
|
|
54
60
|
|
|
61
|
+
def addModelXbrl(self, modelXbrl: ModelXbrl) -> None:
|
|
62
|
+
self._loadedModelXbrls.append(modelXbrl)
|
|
63
|
+
self.setDeiValues(modelXbrl)
|
|
64
|
+
|
|
55
65
|
@lru_cache(1)
|
|
56
66
|
def getCoverItemRequirements(self, jsonPath: Path) -> CoverItemRequirements:
|
|
57
67
|
return CoverItemRequirements(jsonPath)
|
|
@@ -60,6 +70,9 @@ class ControllerPluginData(PluginData):
|
|
|
60
70
|
def getDeiRequirements(self, csvPath: Path, deiItems: tuple[QName, ...], filingFormats: tuple[FilingFormat, ...]) -> DeiRequirements:
|
|
61
71
|
return DeiRequirements(csvPath, deiItems, filingFormats)
|
|
62
72
|
|
|
73
|
+
def getDeiValue(self, localName: str) -> TypeXValue:
|
|
74
|
+
return self._deiValues.get(localName)
|
|
75
|
+
|
|
63
76
|
def getManifestInstances(self) -> list[ManifestInstance]:
|
|
64
77
|
"""
|
|
65
78
|
Retrieve all loaded manifest instances.
|
|
@@ -159,6 +172,28 @@ class ControllerPluginData(PluginData):
|
|
|
159
172
|
def getUsedFilepaths(self) -> frozenset[Path]:
|
|
160
173
|
return frozenset(self._usedFilepaths)
|
|
161
174
|
|
|
175
|
+
def isConsolidated(self) -> bool | None:
|
|
176
|
+
"""
|
|
177
|
+
Is this a consolidated (not individual) filing?
|
|
178
|
+
Looks for the DEI fact 'WhetherConsolidatedFinancialStatementsArePreparedDEI'
|
|
179
|
+
within PublicDoc instances. If an explicit True/False value is found, it is returned.
|
|
180
|
+
If no non-nil value exists, None is returned, which indicates a not applicable state.
|
|
181
|
+
:return:
|
|
182
|
+
"""
|
|
183
|
+
for modelXbrl in self.loadedModelXbrls:
|
|
184
|
+
manifestInstance = self.getManifestInstance(modelXbrl)
|
|
185
|
+
if manifestInstance is None:
|
|
186
|
+
continue
|
|
187
|
+
if manifestInstance.type != ReportFolderType.PUBLIC_DOC.value:
|
|
188
|
+
continue
|
|
189
|
+
facts = modelXbrl.factsByLocalName.get('WhetherConsolidatedFinancialStatementsArePreparedDEI', set())
|
|
190
|
+
for fact in facts:
|
|
191
|
+
if fact.xValue == True:
|
|
192
|
+
return True
|
|
193
|
+
if fact.xValue == False:
|
|
194
|
+
return False
|
|
195
|
+
return None
|
|
196
|
+
|
|
162
197
|
@lru_cache(1)
|
|
163
198
|
def isUpload(self, fileSource: FileSource) -> bool:
|
|
164
199
|
fileSource.open() # Make sure file source is open
|
|
@@ -173,13 +208,23 @@ class ControllerPluginData(PluginData):
|
|
|
173
208
|
return False
|
|
174
209
|
return True
|
|
175
210
|
|
|
176
|
-
|
|
211
|
+
@property
|
|
212
|
+
def loadedModelXbrls(self) -> list[ModelXbrl]:
|
|
213
|
+
"""
|
|
214
|
+
TODO: Only necessary because cntlr.modelManager.loadedModelXbrls is not reliable
|
|
215
|
+
in the current conformance suite runner. Remove when that is fixed/replaced.
|
|
216
|
+
"""
|
|
217
|
+
return self._loadedModelXbrls
|
|
218
|
+
|
|
219
|
+
@lru_cache(1)
|
|
220
|
+
def getManifestInstance(self, modelXbrl: ModelXbrl) -> ManifestInstance | None:
|
|
177
221
|
"""
|
|
178
222
|
Match a manifest instance based on the provided ixdsDocUrls.
|
|
179
223
|
A one-to-one mapping must exist between the model's IXDS document URLs and the manifest instance's IXBRL files.
|
|
180
224
|
:param ixdsDocUrls: A model's list of IXDS document URLs.
|
|
181
225
|
:return: A matching ManifestInstance if found, otherwise None.
|
|
182
226
|
"""
|
|
227
|
+
ixdsDocUrls = modelXbrl.ixdsDocUrls
|
|
183
228
|
modelUrls = set(ixdsDocUrls)
|
|
184
229
|
matchedInstance = None
|
|
185
230
|
for instance in self._manifestInstancesById.values():
|
|
@@ -204,6 +249,27 @@ class ControllerPluginData(PluginData):
|
|
|
204
249
|
break
|
|
205
250
|
return matchedInstance
|
|
206
251
|
|
|
252
|
+
def setDeiValue(self, localName: str, value: TypeXValue) -> None:
|
|
253
|
+
if localName in self._deiValues:
|
|
254
|
+
# Duplicate DEI values will be caught by validations.
|
|
255
|
+
return
|
|
256
|
+
self._deiValues[localName] = value
|
|
257
|
+
|
|
258
|
+
def setDeiValues(self, modelXbrl: ModelXbrl) -> None:
|
|
259
|
+
"""
|
|
260
|
+
Set DEI values from the provided modelXbrl.
|
|
261
|
+
Some EDINET validations rely on both DEI values defined in one instance
|
|
262
|
+
and other values in a separate instance, so we collect DEI values from
|
|
263
|
+
all instances and collect them at a controller level.
|
|
264
|
+
:param modelXbrl:
|
|
265
|
+
:return:
|
|
266
|
+
"""
|
|
267
|
+
for localName in DEI_LOCAL_NAMES:
|
|
268
|
+
for fact in modelXbrl.factsByLocalName.get(localName, ()):
|
|
269
|
+
if fact.isNil or fact.xValid < VALID:
|
|
270
|
+
continue
|
|
271
|
+
self.setDeiValue(localName, fact.xValue)
|
|
272
|
+
|
|
207
273
|
def addUsedFilepath(self, path: Path) -> None:
|
|
208
274
|
self._usedFilepaths.add(path)
|
|
209
275
|
|
|
@@ -568,7 +568,7 @@ class PluginValidationDataExtension(PluginData):
|
|
|
568
568
|
@lru_cache(1)
|
|
569
569
|
def getManifestInstance(self, modelXbrl: ModelXbrl) -> ManifestInstance | None:
|
|
570
570
|
controllerPluginData = ControllerPluginData.get(modelXbrl.modelManager.cntlr, self.name)
|
|
571
|
-
return controllerPluginData.
|
|
571
|
+
return controllerPluginData.getManifestInstance(modelXbrl)
|
|
572
572
|
|
|
573
573
|
@lru_cache(1)
|
|
574
574
|
def getProhibitedAttributeElements(self, modelDocument: ModelDocument) -> list[tuple[ModelObject, str]]:
|
|
@@ -9,8 +9,10 @@ from pathlib import Path
|
|
|
9
9
|
from typing import Any
|
|
10
10
|
|
|
11
11
|
from arelle.ModelXbrl import ModelXbrl
|
|
12
|
+
from arelle.ValidateXbrl import ValidateXbrl
|
|
12
13
|
from arelle.Version import authorLabel, copyrightLabel
|
|
13
14
|
from . import Constants
|
|
15
|
+
from .ControllerPluginData import ControllerPluginData
|
|
14
16
|
from .ValidationPluginExtension import ValidationPluginExtension
|
|
15
17
|
from .rules import contexts, edinet, frta, gfm, manifests, upload
|
|
16
18
|
|
|
@@ -84,6 +86,13 @@ def validateFinally(*args: Any, **kwargs: Any) -> None:
|
|
|
84
86
|
return validationPlugin.validateFinally(*args, **kwargs)
|
|
85
87
|
|
|
86
88
|
|
|
89
|
+
def validateXbrlStart(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
|
|
90
|
+
# TODO: See ControllerPluginData.loadedModelXbrls comment
|
|
91
|
+
controllerPluginData = ControllerPluginData.get(val.modelXbrl.modelManager.cntlr, PLUGIN_NAME)
|
|
92
|
+
controllerPluginData.addModelXbrl(val.modelXbrl)
|
|
93
|
+
return validationPlugin.validateXbrlStart(val, *args, **kwargs)
|
|
94
|
+
|
|
95
|
+
|
|
87
96
|
def validateXbrlFinally(*args: Any, **kwargs: Any) -> None:
|
|
88
97
|
return validationPlugin.validateXbrlFinally(*args, **kwargs)
|
|
89
98
|
|
|
@@ -103,5 +112,6 @@ __pluginInfo__ = {
|
|
|
103
112
|
"Validate.Complete": validateComplete,
|
|
104
113
|
"Validate.FileSource": validateFileSource,
|
|
105
114
|
"Validate.XBRL.Finally": validateXbrlFinally,
|
|
115
|
+
'Validate.XBRL.Start': validateXbrlStart,
|
|
106
116
|
"ValidateFormula.Finished": validateFinally,
|
|
107
117
|
}
|
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
"https://xbrl.org/2023/arcrole/summation-item": ["none", "EDINET.EC5700W.GFM.1.7.4"]
|
|
14
14
|
}'
|
|
15
15
|
defaultXmlLang="ja"
|
|
16
|
+
keepOpen="true"
|
|
16
17
|
blockDisallowedReferences="true"
|
|
18
|
+
standardTaxonomiesUrl="edinet-taxonomies.xml"
|
|
17
19
|
validationType="EDINET"
|
|
18
|
-
validTaxonomiesUrl="edinet-taxonomies.xml"
|
|
19
20
|
exclusiveTypesPattern="EFM|GFM|FERC|HMRC|SBR.NL|EBA|EIOPA|ESEF"
|
|
20
21
|
/>
|
|
21
22
|
</DisclosureSystems>
|
|
@@ -3,21 +3,30 @@ See COPYRIGHT.md for copyright information.
|
|
|
3
3
|
"""
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
|
+
import datetime
|
|
6
7
|
from collections import defaultdict
|
|
8
|
+
|
|
9
|
+
from dateutil.relativedelta import relativedelta
|
|
7
10
|
from itertools import chain
|
|
8
|
-
from typing import Any, Iterable
|
|
11
|
+
from typing import Any, Iterable, cast
|
|
9
12
|
|
|
10
13
|
from arelle import XbrlConst
|
|
14
|
+
from arelle.Cntlr import Cntlr
|
|
15
|
+
from arelle.FileSource import FileSource
|
|
11
16
|
from arelle.LinkbaseType import LinkbaseType
|
|
12
17
|
from arelle.ModelDtsObject import ModelConcept
|
|
13
18
|
from arelle.ValidateXbrl import ValidateXbrl
|
|
19
|
+
from arelle.XmlValidateConst import VALID
|
|
14
20
|
from arelle.typing import TypeGetText
|
|
15
21
|
from arelle.utils.PluginHooks import ValidationHook
|
|
16
22
|
from arelle.utils.validate.Decorator import validation
|
|
17
23
|
from arelle.utils.validate.Validation import Validation
|
|
18
|
-
from ..Constants import FINANCIAL_STATEMENT_CONTEXT_ID_PATTERN, CONTEXT_ID_PATTERN
|
|
24
|
+
from ..Constants import FINANCIAL_STATEMENT_CONTEXT_ID_PATTERN, CONTEXT_ID_PATTERN, INDIVIDUAL_CONTEXT_ID_PATTERN
|
|
25
|
+
from ..ContextRequirement import CONTEXT_REQUIREMENTS
|
|
26
|
+
from ..ControllerPluginData import ControllerPluginData
|
|
19
27
|
from ..DisclosureSystems import (DISCLOSURE_SYSTEM_EDINET)
|
|
20
28
|
from ..PluginValidationDataExtension import PluginValidationDataExtension
|
|
29
|
+
from ..ReportFolderType import ReportFolderType
|
|
21
30
|
|
|
22
31
|
_: TypeGetText
|
|
23
32
|
|
|
@@ -150,6 +159,200 @@ def rule_EC8013W(
|
|
|
150
159
|
)
|
|
151
160
|
|
|
152
161
|
|
|
162
|
+
@validation(
|
|
163
|
+
hook=ValidationHook.COMPLETE,
|
|
164
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
165
|
+
)
|
|
166
|
+
def rule_EC8014W(
|
|
167
|
+
pluginData: ControllerPluginData,
|
|
168
|
+
cntlr: Cntlr,
|
|
169
|
+
fileSource: FileSource,
|
|
170
|
+
*args: Any,
|
|
171
|
+
**kwargs: Any,
|
|
172
|
+
) -> Iterable[Validation]:
|
|
173
|
+
"""
|
|
174
|
+
EDINET.EC8014W: For individual (non-consolidated) reports, there must be a context that represents an individual.
|
|
175
|
+
|
|
176
|
+
Individual reports identified by presence of WhetherConsolidatedFinancialStatementsArePreparedDEI with False value.
|
|
177
|
+
Individual context identified by context ID matching pattern with "_NonConsolidatedMember".
|
|
178
|
+
"""
|
|
179
|
+
if pluginData.isConsolidated() != False:
|
|
180
|
+
return
|
|
181
|
+
if not any(
|
|
182
|
+
INDIVIDUAL_CONTEXT_ID_PATTERN.fullmatch(contextID)
|
|
183
|
+
for modelXbrl in pluginData.loadedModelXbrls
|
|
184
|
+
for contextID in modelXbrl.contexts
|
|
185
|
+
):
|
|
186
|
+
yield Validation.warning(
|
|
187
|
+
codes='EDINET.EC8014W',
|
|
188
|
+
msg=_("There is no context ID in the inline XBRL file that represents an individual. "
|
|
189
|
+
"Please set a context ID that indicates individual financial "
|
|
190
|
+
"statements in the inline XBRL file. "
|
|
191
|
+
"If you are not including individual financial statements, "
|
|
192
|
+
"please check the \"WhetherConsolidatedFinancialStatementsArePreparedDEI\" "
|
|
193
|
+
"value of the DEI information."),
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@validation(
|
|
198
|
+
hook=ValidationHook.COMPLETE,
|
|
199
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
200
|
+
)
|
|
201
|
+
def rule_EC8015W(
|
|
202
|
+
pluginData: ControllerPluginData,
|
|
203
|
+
cntlr: Cntlr,
|
|
204
|
+
fileSource: FileSource,
|
|
205
|
+
*args: Any,
|
|
206
|
+
**kwargs: Any,
|
|
207
|
+
) -> Iterable[Validation]:
|
|
208
|
+
"""
|
|
209
|
+
EDINET.EC8015W: For consolidated reports, there must not be a context that represents an individual.
|
|
210
|
+
|
|
211
|
+
Consolidated reports identified by presence of WhetherConsolidatedFinancialStatementsArePreparedDEI with True value.
|
|
212
|
+
Individual context identified by context ID matching pattern with "_NonConsolidatedMember".
|
|
213
|
+
"""
|
|
214
|
+
if pluginData.isConsolidated() != True:
|
|
215
|
+
return
|
|
216
|
+
individualContexts = [
|
|
217
|
+
context
|
|
218
|
+
for modelXbrl in pluginData.loadedModelXbrls
|
|
219
|
+
for contextId, context in modelXbrl.contexts.items()
|
|
220
|
+
if INDIVIDUAL_CONTEXT_ID_PATTERN.fullmatch(contextId)
|
|
221
|
+
]
|
|
222
|
+
if len(individualContexts) > 0:
|
|
223
|
+
yield Validation.warning(
|
|
224
|
+
codes='EDINET.EC8015W',
|
|
225
|
+
msg=_("There is a context ID in the inline XBRL file that represents an individual. "
|
|
226
|
+
"If you do not want to enter information related to individual financial statements, "
|
|
227
|
+
"delete the context ID that indicates individual. "
|
|
228
|
+
"If you want to enter individual financial statements, "
|
|
229
|
+
"check the \"WhetherConsolidatedFinancialStatementsArePreparedDEI\" status in the DEI "
|
|
230
|
+
"information. "
|
|
231
|
+
"* If there is a change from non-consolidated to consolidated, even if the data content "
|
|
232
|
+
"is normal, it may be recognized as an exception and a warning may be displayed."),
|
|
233
|
+
modelObject=individualContexts,
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
@validation(
|
|
238
|
+
hook=ValidationHook.COMPLETE,
|
|
239
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
240
|
+
)
|
|
241
|
+
def rule_contextDeiRequirements(
|
|
242
|
+
pluginData: ControllerPluginData,
|
|
243
|
+
cntlr: Cntlr,
|
|
244
|
+
fileSource: FileSource,
|
|
245
|
+
*args: Any,
|
|
246
|
+
**kwargs: Any,
|
|
247
|
+
) -> Iterable[Validation]:
|
|
248
|
+
"""
|
|
249
|
+
EDINET.EC8018W, EDINET.EC8019W, and EDINET.EC8020W assert that context instant, startDate, and endDate values
|
|
250
|
+
(respectively) match certain DEI values based on the presence of DEI values and patterns in the context ID.
|
|
251
|
+
See section 3-4-2 in the Validation Guidelines.
|
|
252
|
+
"""
|
|
253
|
+
for modelXbrl in pluginData.loadedModelXbrls:
|
|
254
|
+
for contextId, context in modelXbrl.contexts.items():
|
|
255
|
+
for contextDeiRequirement in CONTEXT_REQUIREMENTS:
|
|
256
|
+
if not contextId.startswith(contextDeiRequirement.contextId):
|
|
257
|
+
continue
|
|
258
|
+
if contextDeiRequirement.elementDoesNotExist is not None:
|
|
259
|
+
if pluginData.getDeiValue(contextDeiRequirement.elementDoesNotExist) is not None:
|
|
260
|
+
continue
|
|
261
|
+
if contextDeiRequirement.elementExists is not None:
|
|
262
|
+
if pluginData.getDeiValue(contextDeiRequirement.elementExists) is None:
|
|
263
|
+
continue
|
|
264
|
+
|
|
265
|
+
if contextDeiRequirement.element == 'startDate':
|
|
266
|
+
contextValue = context.startDatetime
|
|
267
|
+
code = "EDINET.EC8019W"
|
|
268
|
+
elif contextDeiRequirement.element == 'endDate':
|
|
269
|
+
contextValue = context.endDatetime
|
|
270
|
+
code = "EDINET.EC8020W"
|
|
271
|
+
else:
|
|
272
|
+
assert contextDeiRequirement.element == 'instant'
|
|
273
|
+
contextValue = context.instantDatetime
|
|
274
|
+
code = "EDINET.EC8018W"
|
|
275
|
+
|
|
276
|
+
deiValue = pluginData.getDeiValue(contextDeiRequirement.elementMatch)
|
|
277
|
+
if deiValue is None:
|
|
278
|
+
continue
|
|
279
|
+
deiValue = cast(datetime.datetime, deiValue)
|
|
280
|
+
|
|
281
|
+
if contextDeiRequirement.element in ('instant', 'endDate'):
|
|
282
|
+
# Instant and end dates are parsed as the beginning of the day after the date specified by the value
|
|
283
|
+
# DEI values need to be adjusted by 1 day to match this.
|
|
284
|
+
deiValue = cast(datetime.datetime, deiValue) + datetime.timedelta(1)
|
|
285
|
+
if contextDeiRequirement.dayAdjustment:
|
|
286
|
+
deiValue = cast(datetime.datetime, deiValue) + datetime.timedelta(contextDeiRequirement.dayAdjustment)
|
|
287
|
+
|
|
288
|
+
if contextValue != deiValue:
|
|
289
|
+
yield Validation.warning(
|
|
290
|
+
codes=code,
|
|
291
|
+
msg=_("The context %(element)s element does not match the information in DEI "
|
|
292
|
+
"\"%(elementMatch)s\". "
|
|
293
|
+
"Context ID: '%(contextId)s'. "
|
|
294
|
+
"DEI value: '%(deiValue)s'. "
|
|
295
|
+
"Context value: '%(contextValue)s'. "
|
|
296
|
+
"Please set the same value for the %(element)s element value of the corresponding "
|
|
297
|
+
"context ID and the DEI information value."),
|
|
298
|
+
element=contextDeiRequirement.element,
|
|
299
|
+
elementMatch=contextDeiRequirement.elementMatch,
|
|
300
|
+
contextId=contextId,
|
|
301
|
+
deiValue=deiValue.date().isoformat(),
|
|
302
|
+
contextValue=contextValue.date().isoformat(),
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
@validation(
|
|
307
|
+
hook=ValidationHook.COMPLETE,
|
|
308
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
309
|
+
)
|
|
310
|
+
def rule_EC8021W(
|
|
311
|
+
pluginData: ControllerPluginData,
|
|
312
|
+
cntlr: Cntlr,
|
|
313
|
+
fileSource: FileSource,
|
|
314
|
+
*args: Any,
|
|
315
|
+
**kwargs: Any,
|
|
316
|
+
) -> Iterable[Validation]:
|
|
317
|
+
"""
|
|
318
|
+
EDINET.EC8021W: "EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI" or "CurrentPeriodEndDateDEI"
|
|
319
|
+
must not be more than one year earlier than "FilingDateCoverPage".
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
If the "EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI" of the DEI information is present,
|
|
323
|
+
then its value must not be more than one year earlier than "FilingDateCoverPage".
|
|
324
|
+
If "EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI" is not present, but "CurrentPeriodEndDateDEI"
|
|
325
|
+
is present, then its value must not be more than one year earlier than "FilingDateCoverPage".
|
|
326
|
+
"""
|
|
327
|
+
localName = 'EndDateOfQuarterlyOrSemiAnnualPeriodOfNextFiscalYearDEI'
|
|
328
|
+
targetDate = pluginData.getDeiValue(localName)
|
|
329
|
+
if not isinstance(targetDate, datetime.datetime):
|
|
330
|
+
localName = 'CurrentPeriodEndDateDEI'
|
|
331
|
+
targetDate = pluginData.getDeiValue(localName)
|
|
332
|
+
if not isinstance(targetDate, datetime.datetime):
|
|
333
|
+
return
|
|
334
|
+
compareDate = cast(datetime.datetime, targetDate + relativedelta(years=1))
|
|
335
|
+
for modelXbrl in pluginData.loadedModelXbrls:
|
|
336
|
+
for fact in modelXbrl.factsByLocalName.get('FilingDateCoverPage', set()):
|
|
337
|
+
if fact.isNil or fact.xValid < VALID:
|
|
338
|
+
continue
|
|
339
|
+
submissionDate = cast(datetime.datetime, fact.xValue)
|
|
340
|
+
if compareDate < submissionDate:
|
|
341
|
+
yield Validation.warning(
|
|
342
|
+
codes='EDINET.EC8021W',
|
|
343
|
+
msg=_("The DEI '%(localName)s' information is set to a date that is "
|
|
344
|
+
"more than one year earlier than 'FilingDateCoverPage'. "
|
|
345
|
+
"Please set the '%(localName)s' value to a value that is less "
|
|
346
|
+
"than one year earlier than the value of 'FilingDateCoverPage'. "
|
|
347
|
+
"%(localName)s: '%(targetDate)s'. "
|
|
348
|
+
"FilingDateCoverPage: '%(submissionDate)s'. "),
|
|
349
|
+
localName=localName,
|
|
350
|
+
targetDate=targetDate.date().isoformat(),
|
|
351
|
+
submissionDate=submissionDate.date().isoformat(),
|
|
352
|
+
modelObject=fact,
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
|
|
153
356
|
@validation(
|
|
154
357
|
hook=ValidationHook.XBRL_FINALLY,
|
|
155
358
|
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
@@ -231,8 +231,8 @@ def rule_frta_4_2_4(
|
|
|
231
231
|
if rootElt.get('elementFormDefault') != 'qualified' or rootElt.get('attributeFormDefault') != 'unqualified':
|
|
232
232
|
yield Validation.warning(
|
|
233
233
|
codes='EDINET.EC5710W.FRTA.4.2.4',
|
|
234
|
-
msg=_("The XMLSchema root in taxonomy schema files must have the 'elementFormDefault'
|
|
235
|
-
"'
|
|
234
|
+
msg=_("The XMLSchema root in taxonomy schema files must have the 'elementFormDefault' attribute set as "
|
|
235
|
+
"'qualified' and the 'attributeFormDefault' attribute set as 'unqualified'"),
|
|
236
236
|
modelObject=modelDocument,
|
|
237
237
|
)
|
|
238
238
|
formUsages = []
|
|
@@ -728,6 +728,44 @@ def rule_gfm_1_3_1(
|
|
|
728
728
|
)
|
|
729
729
|
|
|
730
730
|
|
|
731
|
+
@validation(
|
|
732
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
733
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
734
|
+
)
|
|
735
|
+
def rule_gfm_1_3_2(
|
|
736
|
+
pluginData: PluginValidationDataExtension,
|
|
737
|
+
val: ValidateXbrl,
|
|
738
|
+
*args: Any,
|
|
739
|
+
**kwargs: Any,
|
|
740
|
+
) -> Iterable[Validation]:
|
|
741
|
+
"""
|
|
742
|
+
EDINET.EC5700W: [GFM 1.3.2] The schemaLocation attribute of the xsd:import element specifying the EDINET taxonomy does not point to a valid location.
|
|
743
|
+
|
|
744
|
+
GFM-1.3.2: If an xsd:import element has a namespace attribute equal to a standard taxonomy
|
|
745
|
+
schema, then its schemaLocation attribute must be the standard taxonomy assigned
|
|
746
|
+
to that namespace.
|
|
747
|
+
"""
|
|
748
|
+
for document in val.modelXbrl.urlDocs.values():
|
|
749
|
+
if not pluginData.isExtensionUri(document.uri, val.modelXbrl) or not document.type == ModelDocument.Type.SCHEMA:
|
|
750
|
+
continue
|
|
751
|
+
for refDoc in document.referencesDocument.values():
|
|
752
|
+
if 'import' not in refDoc.referenceTypes:
|
|
753
|
+
continue
|
|
754
|
+
namespace = refDoc.referringModelObject.attrib.get('namespace')
|
|
755
|
+
schemaLocation = refDoc.referringModelObject.attrib.get('schemaLocation')
|
|
756
|
+
if not namespace or not schemaLocation:
|
|
757
|
+
continue
|
|
758
|
+
expectedXSDLocationSet = val.disclosureSystem.standardTaxonomiesDict.get(namespace)
|
|
759
|
+
if expectedXSDLocationSet is not None and schemaLocation not in expectedXSDLocationSet:
|
|
760
|
+
yield Validation.warning(
|
|
761
|
+
codes='EDINET.EC5700W.GFM.1.3.2',
|
|
762
|
+
msg=_("The schemaLocation attribute of the xsd:import element specifying the EDINET taxonomy does not point to a valid location. "
|
|
763
|
+
"The schemaLocation attribute value '%(schemaLocation)s'."),
|
|
764
|
+
schemaLocation = schemaLocation,
|
|
765
|
+
modelObject = refDoc.referringModelObject
|
|
766
|
+
)
|
|
767
|
+
|
|
768
|
+
|
|
731
769
|
@validation(
|
|
732
770
|
hook=ValidationHook.XBRL_FINALLY,
|
|
733
771
|
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
@@ -1218,6 +1256,39 @@ def rule_gfm_1_3_31(
|
|
|
1218
1256
|
)
|
|
1219
1257
|
|
|
1220
1258
|
|
|
1259
|
+
@validation(
|
|
1260
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
1261
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
1262
|
+
)
|
|
1263
|
+
def rule_gfm_1_4_4(
|
|
1264
|
+
pluginData: PluginValidationDataExtension,
|
|
1265
|
+
val: ValidateXbrl,
|
|
1266
|
+
*args: Any,
|
|
1267
|
+
**kwargs: Any,
|
|
1268
|
+
) -> Iterable[Validation]:
|
|
1269
|
+
"""
|
|
1270
|
+
EDINET.EC5700W: [GFM 1.4.4] If the type attribute is "extended" or "resource", set the xlink:role attribute to the extended link role.
|
|
1271
|
+
|
|
1272
|
+
GFM 1.4.4: The xlink:role attribute of an element with a type="extended" attribute or a
|
|
1273
|
+
type="resource" attribute must be present and must not be empty.
|
|
1274
|
+
"""
|
|
1275
|
+
|
|
1276
|
+
for modelDocument in val.modelXbrl.urlDocs.values():
|
|
1277
|
+
if pluginData.isStandardTaxonomyUrl(modelDocument.uri, val.modelXbrl):
|
|
1278
|
+
continue
|
|
1279
|
+
rootElt = modelDocument.xmlRootElement
|
|
1280
|
+
ns = {'xlink': 'http://www.w3.org/1999/xlink'}
|
|
1281
|
+
for elt in rootElt.xpath('//*[@xlink:type="extended" or @xlink:type="resource"]', namespaces=ns):
|
|
1282
|
+
xlinkRole = elt.get(XbrlConst.qnXlinkRole.clarkNotation)
|
|
1283
|
+
if not xlinkRole:
|
|
1284
|
+
yield Validation.warning(
|
|
1285
|
+
codes='EDINET.EC5700W.GFM.1.4.4',
|
|
1286
|
+
msg=_("If the type attribute is 'extended' or 'resource', set the xlink:role attribute to the extended link role."
|
|
1287
|
+
"%(element)s is missing an xlink:role"),
|
|
1288
|
+
modelObject=elt, element=elt.qname
|
|
1289
|
+
)
|
|
1290
|
+
|
|
1291
|
+
|
|
1221
1292
|
@validation(
|
|
1222
1293
|
hook=ValidationHook.XBRL_FINALLY,
|
|
1223
1294
|
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
@@ -654,6 +654,8 @@ def rule_cover_items(
|
|
|
654
654
|
for elt in rootElt.iterdescendants(ixNStag + "nonNumeric", ixNStag + "nonFraction", ixNStag + "fraction"):
|
|
655
655
|
if not isinstance(elt, ModelFact):
|
|
656
656
|
continue
|
|
657
|
+
if not elt.qname in allCoverItems:
|
|
658
|
+
continue
|
|
657
659
|
if elt.qname in prohibitedCoverItems:
|
|
658
660
|
yield Validation.error(
|
|
659
661
|
codes='EDINET.EC1003E',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
arelle/Aspect.py,sha256=Pn9I91D1os1RTVj6htuxTfRzVMhmVDtrbKvV_zy9xMI,5470
|
|
2
2
|
arelle/BetaFeatures.py,sha256=T_tPac-FiozHyYLCemt0RoHJ1JahUE71L-0tHmIRKpE,858
|
|
3
3
|
arelle/Cntlr.py,sha256=HQ9JfpByqRf-C-ub07ALWMWUO2LIFev8tcmqpXjHW9s,31718
|
|
4
|
-
arelle/CntlrCmdLine.py,sha256=
|
|
4
|
+
arelle/CntlrCmdLine.py,sha256=uzHyES0xyIZ9OSksU8Hg78tfOiHS0t9xhFo-DLpCOK0,88956
|
|
5
5
|
arelle/CntlrComServer.py,sha256=h1KPf31uMbErpxTZn_iklDqUMGFgQnjZkFkFjd8gtLQ,1888
|
|
6
6
|
arelle/CntlrProfiler.py,sha256=2VQJudiUhxryVypxjODx2ccP1-n60icTiWs5lSEokhQ,972
|
|
7
7
|
arelle/CntlrQuickBooks.py,sha256=BMqd5nkNQOZyNFPefkTeWUUDCYNS6BQavaG8k1Lepu4,31543
|
|
@@ -20,9 +20,9 @@ arelle/DialogPluginManager.py,sha256=0Uiy2Dei_49K7jbNnEUTchqcjANPIKNYNIt29iQoV1g
|
|
|
20
20
|
arelle/DialogRssWatch.py,sha256=mjc4pqyFDISY4tQtME0uSRQ3NlcWnNsOsMu9Zj8tTd0,13789
|
|
21
21
|
arelle/DialogURL.py,sha256=JH88OPFf588E8RW90uMaieok7A_4kOAURQ8kHWVhnao,4354
|
|
22
22
|
arelle/DialogUserPassword.py,sha256=kWPlCCihhwvsykDjanME9qBDtv6cxZlsrJyoMqiRep4,13769
|
|
23
|
-
arelle/DisclosureSystem.py,sha256=
|
|
23
|
+
arelle/DisclosureSystem.py,sha256=TA41D_rJGsmHytV2ya4Po2O9pcUnxiLc90qo5CoNaKM,25279
|
|
24
24
|
arelle/ErrorManager.py,sha256=5BrpD9Ej8b-2JFR-GgWL32Oc2qItYnLbZApaToDKt0o,16972
|
|
25
|
-
arelle/FileSource.py,sha256=
|
|
25
|
+
arelle/FileSource.py,sha256=3OsWhyCiPzek72XNWvxmfVCCIYADzUGNRNt8Jh2rCu4,48170
|
|
26
26
|
arelle/FunctionCustom.py,sha256=d1FsBG14eykvpLpgaXpN8IdxnlG54dfGcsXPYfdpA9Q,5880
|
|
27
27
|
arelle/FunctionFn.py,sha256=BcZKah1rpfquSVPwjvknM1pgFXOnK4Hr1e_ArG_mcJY,38058
|
|
28
28
|
arelle/FunctionIxt.py,sha256=8JELGh1l4o8Ul4_G7JgwX8Ebch9it2DblI6OkfL33Cw,80082
|
|
@@ -125,12 +125,12 @@ arelle/XmlValidateConst.py,sha256=U_wN0Q-nWKwf6dKJtcu_83FXPn9c6P8JjzGA5b0w7P0,33
|
|
|
125
125
|
arelle/XmlValidateParticles.py,sha256=Mn6vhFl0ZKC_vag1mBwn1rH_x2jmlusJYqOOuxFPO2k,9231
|
|
126
126
|
arelle/XmlValidateSchema.py,sha256=6frtZOc1Yrx_5yYF6V6oHbScnglWrVbWr6xW4EHtLQI,7428
|
|
127
127
|
arelle/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
128
|
-
arelle/_version.py,sha256=
|
|
128
|
+
arelle/_version.py,sha256=pzNwbLyTDGMLh8oqxsJPGFHbPoDh3ciopU2CzerFPvU,708
|
|
129
129
|
arelle/typing.py,sha256=PRe-Fxwr2SBqYYUVPCJ3E7ddDX0_oOISNdT5Q97EbRM,1246
|
|
130
|
-
arelle/api/Session.py,sha256=
|
|
130
|
+
arelle/api/Session.py,sha256=a71ML4FOkWxeqxQBRu6WUqkUoI4C5R6znzAQcbGR5jg,7902
|
|
131
131
|
arelle/config/creationSoftwareNames.json,sha256=5MK7XUjfDJ9OpRCCHXeOErJ1SlTBZji4WEcEOdOacx0,3128
|
|
132
132
|
arelle/config/disclosuresystems.xml,sha256=8lBSGCOuVsVxttmpqkPlURBzaiGpPm8b8aYtxKRsKnk,16617
|
|
133
|
-
arelle/config/disclosuresystems.xsd,sha256=
|
|
133
|
+
arelle/config/disclosuresystems.xsd,sha256=FiwGkxlkRMgGg45V9b_P1-GiI7b1YVatZd5d50s08pI,4335
|
|
134
134
|
arelle/config/edbody.dtd,sha256=ZGfnEvKbROUpV-aCWeAV3VBuY0QquDIiNzOU-ehgsGk,15557
|
|
135
135
|
arelle/config/empty-instance.xml,sha256=1wRlhlSErE_ro3noF30-WTM0WVOpyKUI9L9POrbt368,390
|
|
136
136
|
arelle/config/erxl.xsd,sha256=27GMbkisl6dA6ZOXZiOtBEwBQPb1fBUc4KOYNVsRo0o,2167
|
|
@@ -312,32 +312,33 @@ 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=
|
|
316
|
-
arelle/plugin/validate/EDINET/
|
|
315
|
+
arelle/plugin/validate/EDINET/Constants.py,sha256=6POa7ftTNQAV1td5WYGbzPGSXpWfAE9bfbXNFnTqfOI,8146
|
|
316
|
+
arelle/plugin/validate/EDINET/ContextRequirement.py,sha256=BejN3uz6TCSzR1qfQm-PZMbkhJlgUH9NVrY2Ux-Ph78,5217
|
|
317
|
+
arelle/plugin/validate/EDINET/ControllerPluginData.py,sha256=8FVFpqh25NaxRKWn5qlK23qD6XDUOvNXRW35O6X9hTk,11599
|
|
317
318
|
arelle/plugin/validate/EDINET/CoverItemRequirements.py,sha256=1YdzlSgc5MlUGyw81Ipv37CLxsdpSw8CO90snjgqLzw,1852
|
|
318
319
|
arelle/plugin/validate/EDINET/DeiRequirements.py,sha256=ncK5ew66Y1rAsNHAMTHhX4jTrE_XOFGHV5okPB0vEYA,4316
|
|
319
320
|
arelle/plugin/validate/EDINET/DisclosureSystems.py,sha256=3rKG42Eg-17Xx_KXU_V5yHW6I3LTwQunvf4a44C9k_4,36
|
|
320
321
|
arelle/plugin/validate/EDINET/FilingFormat.py,sha256=SFZ22zFk6RVIA9dpx3iVLlf2heKfZZqt2ZUXUje4BII,18789
|
|
321
322
|
arelle/plugin/validate/EDINET/FormType.py,sha256=jFqjJACJJ4HhkY1t6Fqei0z6rgvH3Mp-dP04KwQVv3Q,2517
|
|
322
323
|
arelle/plugin/validate/EDINET/ManifestInstance.py,sha256=o6BGlaQHSsn6D0VKH4zn59UscKnjTKlo99kSGfGdYlU,6910
|
|
323
|
-
arelle/plugin/validate/EDINET/PluginValidationDataExtension.py,sha256
|
|
324
|
+
arelle/plugin/validate/EDINET/PluginValidationDataExtension.py,sha256=rFzAR8VbVcCO2IO1qp6TGWpBQd2rsOES-Ta2UOKOmDs,31309
|
|
324
325
|
arelle/plugin/validate/EDINET/ReportFolderType.py,sha256=-p_byoOLP6wAyEiPtSN2iMMl68m5vPh__hHTYgypxH8,4612
|
|
325
326
|
arelle/plugin/validate/EDINET/Statement.py,sha256=CGq8c647pIEBQtOv8AL0U4knT8HofOdK8IaUjHrcOYU,9331
|
|
326
327
|
arelle/plugin/validate/EDINET/TableOfContentsBuilder.py,sha256=-TsPfIdtgOCdvUh-H8EZo3B5A63ti7rIDXHCUnt_us8,21760
|
|
327
328
|
arelle/plugin/validate/EDINET/UploadContents.py,sha256=o29mDoX48M3S2jQqrJO4ZaulltAPt4vD-qdsWTMCUPc,1196
|
|
328
329
|
arelle/plugin/validate/EDINET/ValidationPluginExtension.py,sha256=8LNqvXzNaWP54dShEjet5ely4BnM8ByCSyimKpUx3_s,2577
|
|
329
|
-
arelle/plugin/validate/EDINET/__init__.py,sha256=
|
|
330
|
-
arelle/plugin/validate/EDINET/resources/config.xml,sha256=
|
|
330
|
+
arelle/plugin/validate/EDINET/__init__.py,sha256=kgYOe4C8x-eccMz0SqCfV5Krw_s51heqSvEgKH5pWlw,4247
|
|
331
|
+
arelle/plugin/validate/EDINET/resources/config.xml,sha256=Occs0dAABndvTbyjWK2sM-qeVMAgLuHhM-EFq-T2jTg,1147
|
|
331
332
|
arelle/plugin/validate/EDINET/resources/cover-item-requirements.json,sha256=K5j2HBF-1y0kazWS4EZWs1KCROaCF9ls3MBzXi7Iex0,46970
|
|
332
333
|
arelle/plugin/validate/EDINET/resources/dei-requirements.csv,sha256=8ILKNn8bXbcgG9V0lc8mxorKDKEjJQWLdBQRMvbqtkI,6518
|
|
333
334
|
arelle/plugin/validate/EDINET/resources/edinet-taxonomies.xml,sha256=YXaPu-60CI2s0S-vXcLDEXCrrukEAAHyCEk0QRhrYR8,16772
|
|
334
335
|
arelle/plugin/validate/EDINET/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
335
|
-
arelle/plugin/validate/EDINET/rules/contexts.py,sha256=
|
|
336
|
+
arelle/plugin/validate/EDINET/rules/contexts.py,sha256=fSYIZOWg3Q3cDos17hNAvw1HUVdcZx7IlUFfBJnwjHo,19604
|
|
336
337
|
arelle/plugin/validate/EDINET/rules/edinet.py,sha256=UhvTPfHxDuUitIAPY_4fgirAsjJG5Xfd-gH_4pMd3qI,25449
|
|
337
|
-
arelle/plugin/validate/EDINET/rules/frta.py,sha256=
|
|
338
|
-
arelle/plugin/validate/EDINET/rules/gfm.py,sha256=
|
|
338
|
+
arelle/plugin/validate/EDINET/rules/frta.py,sha256=wpX5zK0IfVVYZR9ELy2g_Z_F1Jzhwd9gh_KZC5ALkLg,12756
|
|
339
|
+
arelle/plugin/validate/EDINET/rules/gfm.py,sha256=CoFDf9yIncroaqtCRRJImnfELf6IU6ORN1c2xvhTj54,89125
|
|
339
340
|
arelle/plugin/validate/EDINET/rules/manifests.py,sha256=MoT9R_a4BzuYdQVbF7RC5wz134Ve68svSdJ3NlpO_AU,4026
|
|
340
|
-
arelle/plugin/validate/EDINET/rules/upload.py,sha256=
|
|
341
|
+
arelle/plugin/validate/EDINET/rules/upload.py,sha256=eI1eJWGCsEqG5jUsbc5VH-PoT2aSMWxlW5aqwZGQ0Fk,50266
|
|
341
342
|
arelle/plugin/validate/ESEF/Const.py,sha256=JujF_XV-_TNsxjGbF-8SQS4OOZIcJ8zhCMnr-C1O5Ho,22660
|
|
342
343
|
arelle/plugin/validate/ESEF/Dimensions.py,sha256=MOJM7vwNPEmV5cu-ZzPrhx3347ZvxgD6643OB2HRnIk,10597
|
|
343
344
|
arelle/plugin/validate/ESEF/Util.py,sha256=QH3btcGqBpr42M7WSKZLSdNXygZaZLfEiEjlxoG21jE,7950
|
|
@@ -687,9 +688,9 @@ arelle/utils/validate/ValidationUtil.py,sha256=hghMQoNuC3ocs6kkNtIQPhh8QwzubwHGt
|
|
|
687
688
|
arelle/utils/validate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
688
689
|
arelle/webserver/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
689
690
|
arelle/webserver/bottle.py,sha256=P-JECd9MCTNcxCnKoDUvGcoi03ezYVOgoWgv2_uH-6M,362
|
|
690
|
-
arelle_release-2.37.
|
|
691
|
-
arelle_release-2.37.
|
|
692
|
-
arelle_release-2.37.
|
|
693
|
-
arelle_release-2.37.
|
|
694
|
-
arelle_release-2.37.
|
|
695
|
-
arelle_release-2.37.
|
|
691
|
+
arelle_release-2.37.64.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
|
|
692
|
+
arelle_release-2.37.64.dist-info/METADATA,sha256=HdYz86IdNk8BDYFcX_4VLej6PIgl6c6B53RCpNa4oA4,9355
|
|
693
|
+
arelle_release-2.37.64.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
694
|
+
arelle_release-2.37.64.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
|
|
695
|
+
arelle_release-2.37.64.dist-info/top_level.txt,sha256=fwU7SYawL4_r-sUMRg7r1nYVGjFMSDvRWx8VGAXEw7w,7
|
|
696
|
+
arelle_release-2.37.64.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|