arelle-release 2.37.56__py3-none-any.whl → 2.37.57__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 +1 -0
- arelle/ErrorManager.py +3 -0
- arelle/ValidateDuplicateFacts.py +1 -1
- arelle/XbrlConst.py +1 -0
- arelle/_version.py +2 -2
- arelle/plugin/validate/EDINET/Constants.py +0 -18
- arelle/plugin/validate/EDINET/ControllerPluginData.py +11 -4
- arelle/plugin/validate/EDINET/CoverPageRequirements.py +118 -0
- arelle/plugin/validate/EDINET/FilingFormat.py +253 -0
- arelle/plugin/validate/EDINET/FormType.py +81 -0
- arelle/plugin/validate/EDINET/PluginValidationDataExtension.py +140 -33
- arelle/plugin/validate/EDINET/resources/cover-page-requirements.csv +27 -0
- arelle/plugin/validate/EDINET/rules/edinet.py +1 -1
- arelle/plugin/validate/EDINET/rules/gfm.py +187 -2
- arelle/plugin/validate/EDINET/rules/upload.py +183 -10
- arelle/plugin/validate/ROS/PluginValidationDataExtension.py +2 -0
- arelle/plugin/validate/ROS/ValidationPluginExtension.py +1 -0
- arelle/plugin/validate/ROS/rules/ros.py +37 -7
- {arelle_release-2.37.56.dist-info → arelle_release-2.37.57.dist-info}/METADATA +1 -1
- {arelle_release-2.37.56.dist-info → arelle_release-2.37.57.dist-info}/RECORD +24 -20
- {arelle_release-2.37.56.dist-info → arelle_release-2.37.57.dist-info}/WHEEL +0 -0
- {arelle_release-2.37.56.dist-info → arelle_release-2.37.57.dist-info}/entry_points.txt +0 -0
- {arelle_release-2.37.56.dist-info → arelle_release-2.37.57.dist-info}/licenses/LICENSE.md +0 -0
- {arelle_release-2.37.56.dist-info → arelle_release-2.37.57.dist-info}/top_level.txt +0 -0
|
@@ -9,14 +9,13 @@ from decimal import Decimal
|
|
|
9
9
|
from functools import lru_cache
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
|
|
12
|
-
from lxml.etree import DTD, XML
|
|
12
|
+
from lxml.etree import DTD, XML
|
|
13
13
|
from operator import attrgetter
|
|
14
14
|
from typing import Callable, Hashable, Iterable, cast
|
|
15
15
|
|
|
16
16
|
import os
|
|
17
17
|
import regex
|
|
18
18
|
|
|
19
|
-
from arelle import UrlUtil
|
|
20
19
|
from arelle.LinkbaseType import LinkbaseType
|
|
21
20
|
from arelle.ModelDocument import Type as ModelDocumentType, ModelDocument
|
|
22
21
|
from arelle.ModelDtsObject import ModelConcept
|
|
@@ -31,8 +30,11 @@ from arelle.XhtmlValidate import htmlEltUriAttrs
|
|
|
31
30
|
from arelle.XmlValidate import VALID
|
|
32
31
|
from arelle.typing import TypeGetText
|
|
33
32
|
from arelle.utils.PluginData import PluginData
|
|
34
|
-
from .Constants import
|
|
33
|
+
from .Constants import xhtmlDtdExtension, PROHIBITED_HTML_TAGS, PROHIBITED_HTML_ATTRIBUTES
|
|
35
34
|
from .ControllerPluginData import ControllerPluginData
|
|
35
|
+
from .CoverPageRequirements import CoverPageRequirements, COVER_PAGE_ITEM_LOCAL_NAMES
|
|
36
|
+
from .FilingFormat import FilingFormat, FILING_FORMATS
|
|
37
|
+
from .FormType import FormType
|
|
36
38
|
from .ManifestInstance import ManifestInstance
|
|
37
39
|
from .Statement import Statement, STATEMENTS, BalanceSheet, StatementInstance, StatementType
|
|
38
40
|
from .UploadContents import UploadContents
|
|
@@ -58,35 +60,59 @@ class PluginValidationDataExtension(PluginData):
|
|
|
58
60
|
consolidatedOrNonConsolidatedAxisQn: QName
|
|
59
61
|
documentTypeDeiQn: QName
|
|
60
62
|
jpcrpEsrFilingDateCoverPageQn: QName
|
|
63
|
+
jpcrpEsrNamespace: str
|
|
61
64
|
jpcrpFilingDateCoverPageQn: QName
|
|
65
|
+
jpcrpNamespace: str
|
|
66
|
+
jpdeiNamespace: str
|
|
67
|
+
jpigpNamespace: str
|
|
68
|
+
jppfsNamespace: str
|
|
62
69
|
jpspsFilingDateCoverPageQn: QName
|
|
70
|
+
jpspsNamespace: str
|
|
63
71
|
nonConsolidatedMemberQn: QName
|
|
64
72
|
ratioOfFemaleDirectorsAndOtherOfficersQn: QName
|
|
65
73
|
|
|
66
74
|
contextIdPattern: regex.Pattern[str]
|
|
75
|
+
coverPageItems: tuple[QName, ...]
|
|
76
|
+
coverPageRequirementsPath: Path
|
|
77
|
+
coverPageTitleQns: tuple[QName, ...]
|
|
67
78
|
|
|
68
79
|
_uriReferences: list[UriReference]
|
|
69
80
|
|
|
70
81
|
def __init__(self, name: str, validateXbrl: ValidateXbrl):
|
|
71
82
|
super().__init__(name)
|
|
72
|
-
|
|
83
|
+
|
|
84
|
+
# Namespaces
|
|
85
|
+
self.jpcrpEsrNamespace = "http://disclosure.edinet-fsa.go.jp/taxonomy/jpcrp-esr/2024-11-01/jpcrp-esr_cor"
|
|
73
86
|
self.jpcrpNamespace = 'http://disclosure.edinet-fsa.go.jp/taxonomy/jpcrp/2024-11-01/jpcrp_cor'
|
|
74
|
-
jpdeiNamespace = 'http://disclosure.edinet-fsa.go.jp/taxonomy/jpdei/2013-08-31/jpdei_cor'
|
|
75
|
-
jpigpNamespace = "http://disclosure.edinet-fsa.go.jp/taxonomy/jpigp/2024-11-01/jpigp_cor"
|
|
76
|
-
jppfsNamespace = "http://disclosure.edinet-fsa.go.jp/taxonomy/jppfs/2024-11-01/jppfs_cor"
|
|
77
|
-
jpspsNamespace = 'http://disclosure.edinet-fsa.go.jp/taxonomy/jpsps/2024-11-01/jpsps_cor'
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
self.
|
|
81
|
-
self.
|
|
87
|
+
self.jpdeiNamespace = 'http://disclosure.edinet-fsa.go.jp/taxonomy/jpdei/2013-08-31/jpdei_cor'
|
|
88
|
+
self.jpigpNamespace = "http://disclosure.edinet-fsa.go.jp/taxonomy/jpigp/2024-11-01/jpigp_cor"
|
|
89
|
+
self.jppfsNamespace = "http://disclosure.edinet-fsa.go.jp/taxonomy/jppfs/2024-11-01/jppfs_cor"
|
|
90
|
+
self.jpspsNamespace = 'http://disclosure.edinet-fsa.go.jp/taxonomy/jpsps/2024-11-01/jpsps_cor'
|
|
91
|
+
|
|
92
|
+
# QNames
|
|
93
|
+
self.accountingStandardsDeiQn = qname(self.jpdeiNamespace, 'AccountingStandardsDEI')
|
|
94
|
+
self.assetsIfrsQn = qname(self.jpigpNamespace, 'AssetsIFRS')
|
|
95
|
+
self.consolidatedOrNonConsolidatedAxisQn = qname(self.jppfsNamespace, 'ConsolidatedOrNonConsolidatedAxis')
|
|
96
|
+
self.documentTypeDeiQn = qname(self.jpdeiNamespace, 'DocumentTypeDEI')
|
|
82
97
|
self.issuedSharesTotalNumberOfSharesEtcQn = qname(self.jpcrpNamespace, 'IssuedSharesTotalNumberOfSharesEtcTextBlock')
|
|
83
|
-
self.jpcrpEsrFilingDateCoverPageQn = qname(jpcrpEsrNamespace, 'FilingDateCoverPage')
|
|
98
|
+
self.jpcrpEsrFilingDateCoverPageQn = qname(self.jpcrpEsrNamespace, 'FilingDateCoverPage')
|
|
84
99
|
self.jpcrpFilingDateCoverPageQn = qname(self.jpcrpNamespace, 'FilingDateCoverPage')
|
|
85
|
-
self.jpspsFilingDateCoverPageQn = qname(jpspsNamespace, 'FilingDateCoverPage')
|
|
86
|
-
self.nonConsolidatedMemberQn = qname(jppfsNamespace, "NonConsolidatedMember")
|
|
100
|
+
self.jpspsFilingDateCoverPageQn = qname(self.jpspsNamespace, 'FilingDateCoverPage')
|
|
101
|
+
self.nonConsolidatedMemberQn = qname(self.jppfsNamespace, "NonConsolidatedMember")
|
|
87
102
|
self.ratioOfFemaleDirectorsAndOtherOfficersQn = qname(self.jpcrpNamespace, "RatioOfFemaleDirectorsAndOtherOfficers")
|
|
88
103
|
|
|
89
104
|
self.contextIdPattern = regex.compile(r'(Prior[1-9]Year|CurrentYear|Prior[1-9]Interim|Interim)(Duration|Instant)')
|
|
105
|
+
self.coverPageItems = tuple(
|
|
106
|
+
qname(self.jpdeiNamespace, localName)
|
|
107
|
+
for localName in COVER_PAGE_ITEM_LOCAL_NAMES
|
|
108
|
+
)
|
|
109
|
+
self.coverPageRequirementsPath = Path(__file__).parent / "resources" / "cover-page-requirements.csv"
|
|
110
|
+
self.coverPageTitleQns = (
|
|
111
|
+
qname(self.jpspsNamespace, "DocumentTitleAnnualSecuritiesReportCoverPage"),
|
|
112
|
+
qname(self.jpcrpNamespace, "DocumentTitleCoverPage"),
|
|
113
|
+
qname(self.jpcrpEsrNamespace, "DocumentTitleCoverPage"),
|
|
114
|
+
qname(self.jpspsNamespace, "DocumentTitleCoverPage"),
|
|
115
|
+
)
|
|
90
116
|
|
|
91
117
|
self._uriReferences = []
|
|
92
118
|
self._initialize(validateXbrl.modelXbrl)
|
|
@@ -140,17 +166,22 @@ class PluginValidationDataExtension(PluginData):
|
|
|
140
166
|
|
|
141
167
|
@lru_cache(1)
|
|
142
168
|
def isCorporateForm(self, modelXbrl: ModelXbrl) -> bool:
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
169
|
+
formTypes = self.getFormTypes(modelXbrl)
|
|
170
|
+
return any(
|
|
171
|
+
formType.isCorporateForm
|
|
172
|
+
for formType in formTypes
|
|
173
|
+
)
|
|
147
174
|
|
|
148
175
|
def isCorporateReport(self, modelXbrl: ModelXbrl) -> bool:
|
|
149
176
|
return self.jpcrpNamespace in modelXbrl.namespaceDocs
|
|
150
177
|
|
|
178
|
+
@lru_cache(1)
|
|
151
179
|
def isStockForm(self, modelXbrl: ModelXbrl) -> bool:
|
|
152
|
-
|
|
153
|
-
return any(
|
|
180
|
+
formTypes = self.getFormTypes(modelXbrl)
|
|
181
|
+
return any(
|
|
182
|
+
formType.isStockReport
|
|
183
|
+
for formType in formTypes
|
|
184
|
+
)
|
|
154
185
|
|
|
155
186
|
def getBalanceSheets(self, modelXbrl: ModelXbrl, statement: Statement) -> list[BalanceSheet]:
|
|
156
187
|
"""
|
|
@@ -207,6 +238,10 @@ class PluginValidationDataExtension(PluginData):
|
|
|
207
238
|
)
|
|
208
239
|
return balanceSheets
|
|
209
240
|
|
|
241
|
+
def getCoverPageRequirements(self, modelXbrl: ModelXbrl) -> CoverPageRequirements:
|
|
242
|
+
controllerPluginData = ControllerPluginData.get(modelXbrl.modelManager.cntlr, self.name)
|
|
243
|
+
return controllerPluginData.getCoverPageRequirements(self.coverPageRequirementsPath, self.coverPageItems, FILING_FORMATS)
|
|
244
|
+
|
|
210
245
|
def getProblematicTextBlocks(self, modelXbrl: ModelXbrl) -> list[ModelInlineFact]:
|
|
211
246
|
problematicTextBlocks: list[ModelInlineFact] = []
|
|
212
247
|
dtd = DTD(os.path.join(modelXbrl.modelManager.cntlr.configDir, xhtmlDtdExtension))
|
|
@@ -248,15 +283,6 @@ class PluginValidationDataExtension(PluginData):
|
|
|
248
283
|
def getDeduplicatedFacts(self, modelXbrl: ModelXbrl) -> list[ModelFact]:
|
|
249
284
|
return getDeduplicatedFacts(modelXbrl, DeduplicationType.CONSISTENT_PAIRS)
|
|
250
285
|
|
|
251
|
-
@lru_cache(1)
|
|
252
|
-
def getDocumentTypes(self, modelXbrl: ModelXbrl) -> set[str]:
|
|
253
|
-
documentFacts = modelXbrl.factsByQname.get(self.documentTypeDeiQn, set())
|
|
254
|
-
documentTypes = set()
|
|
255
|
-
for fact in documentFacts:
|
|
256
|
-
if fact.xValid >= VALID:
|
|
257
|
-
documentTypes.add(fact.textValue)
|
|
258
|
-
return documentTypes
|
|
259
|
-
|
|
260
286
|
def getFactsByContextAndUnit(
|
|
261
287
|
self, modelXbrl: ModelXbrl,
|
|
262
288
|
getContextKey: Callable[[ModelContext], Hashable],
|
|
@@ -297,6 +323,80 @@ class PluginValidationDataExtension(PluginData):
|
|
|
297
323
|
if isinstance(elt, (ModelObject, LinkPrototype))
|
|
298
324
|
]
|
|
299
325
|
|
|
326
|
+
@lru_cache(1)
|
|
327
|
+
def getFilingFormat(self, modelXbrl: ModelXbrl) -> FilingFormat | None:
|
|
328
|
+
# This function attempts to identify the filing format based on form number and title concepts.
|
|
329
|
+
# The provided form number value directly informs the format.
|
|
330
|
+
# However, the document title is not necessarily an explicit setting of the format's
|
|
331
|
+
# document type. In the samples available to us and in a handful of public filings,
|
|
332
|
+
# it is effective to match the first segment of the title value against document type
|
|
333
|
+
# values assigned to the various FilingFormats. This may only be by coincidence or convention.
|
|
334
|
+
# If it doesn't end up being reliable, we may need to find another way to identify the form.
|
|
335
|
+
# For example, by disclosure system selection or CLI argument.
|
|
336
|
+
documentTitleFacts = []
|
|
337
|
+
for qname in self.coverPageTitleQns:
|
|
338
|
+
for fact in self.iterValidNonNilFacts(modelXbrl, qname):
|
|
339
|
+
documentTitleFacts.append(fact)
|
|
340
|
+
formTypes = self.getFormTypes(modelXbrl)
|
|
341
|
+
filingFormats = []
|
|
342
|
+
for filingFormatIndex, filingFormat in enumerate(FILING_FORMATS):
|
|
343
|
+
if filingFormat.formType not in formTypes:
|
|
344
|
+
continue
|
|
345
|
+
prefixes = {taxonomy.value for taxonomy in filingFormat.taxonomies}
|
|
346
|
+
if not any(
|
|
347
|
+
str(fact.xValue).startswith(filingFormat.documentType.value) and
|
|
348
|
+
fact.concept.qname.prefix.split('_')[0] in prefixes
|
|
349
|
+
for fact in documentTitleFacts
|
|
350
|
+
):
|
|
351
|
+
continue
|
|
352
|
+
filingFormats.append((filingFormat, filingFormatIndex))
|
|
353
|
+
if len(filingFormats) == 0:
|
|
354
|
+
modelXbrl.error(
|
|
355
|
+
"arelle:NoMatchingEdinetFormat",
|
|
356
|
+
_("No matching EDINET filing formats could be identified based on form "
|
|
357
|
+
"type (%(formTypes)s) and title."),
|
|
358
|
+
formTypes=formTypes,
|
|
359
|
+
modelObject=documentTitleFacts,
|
|
360
|
+
)
|
|
361
|
+
return None
|
|
362
|
+
if len(filingFormats) > 1:
|
|
363
|
+
formatIndexes = [str(idx + 1) for _, idx in filingFormats]
|
|
364
|
+
modelXbrl.error(
|
|
365
|
+
"arelle:MultipleMatchingEdinetFormats",
|
|
366
|
+
_("Multiple EDINET filing formats (%(formatIndexes)s) matched based on form "
|
|
367
|
+
"type %(formTypes)s and title."),
|
|
368
|
+
formatIndexes=formatIndexes,
|
|
369
|
+
formTypes=formTypes,
|
|
370
|
+
modelObject=documentTitleFacts,
|
|
371
|
+
)
|
|
372
|
+
return None
|
|
373
|
+
filingFormat, filingFormatIndex = filingFormats[0]
|
|
374
|
+
modelXbrl.modelManager.cntlr.addToLog("Identified filing format: #{}, {}, {}, {}, {}".format(
|
|
375
|
+
filingFormatIndex + 1,
|
|
376
|
+
filingFormat.ordinance.value,
|
|
377
|
+
filingFormat.documentType.value,
|
|
378
|
+
filingFormat.formType.value,
|
|
379
|
+
', '.join(taxonomy.value for taxonomy in filingFormat.taxonomies)
|
|
380
|
+
), messageCode="info")
|
|
381
|
+
return filingFormat
|
|
382
|
+
|
|
383
|
+
@lru_cache(1)
|
|
384
|
+
def getFormTypes(self, modelXbrl: ModelXbrl) -> set[FormType]:
|
|
385
|
+
"""
|
|
386
|
+
Retrieves form type values from the instance.
|
|
387
|
+
Note that the underlying concept is labeled "DocumentTypeDEI",
|
|
388
|
+
but "Document Type" refers to something else in EDINET documentation.
|
|
389
|
+
In practice, the value of this field is the form number / form type.
|
|
390
|
+
:param modelXbrl: Instance to get form types from.
|
|
391
|
+
:return: Set of discovered form types.
|
|
392
|
+
"""
|
|
393
|
+
formTypes = set()
|
|
394
|
+
for fact in self.iterValidNonNilFacts(modelXbrl, self.documentTypeDeiQn):
|
|
395
|
+
formType = FormType.parse(fact.textValue)
|
|
396
|
+
if formType is not None:
|
|
397
|
+
formTypes.add(formType)
|
|
398
|
+
return formTypes
|
|
399
|
+
|
|
300
400
|
@lru_cache(1)
|
|
301
401
|
def getManifestInstance(self, modelXbrl: ModelXbrl) -> ManifestInstance | None:
|
|
302
402
|
controllerPluginData = ControllerPluginData.get(modelXbrl.modelManager.cntlr, self.name)
|
|
@@ -350,10 +450,17 @@ class PluginValidationDataExtension(PluginData):
|
|
|
350
450
|
def isStandardTaxonomyUrl(self, uri: str, modelXbrl: ModelXbrl) -> bool:
|
|
351
451
|
return modelXbrl.modelManager.disclosureSystem.hrefValidForDisclosureSystem(uri)
|
|
352
452
|
|
|
453
|
+
def iterFacts(self, modelXbrl: ModelXbrl, qname: QName) -> Iterable[ModelFact]:
|
|
454
|
+
yield from modelXbrl.factsByQname.get(qname, set())
|
|
455
|
+
|
|
456
|
+
def iterValidFacts(self, modelXbrl: ModelXbrl, qname: QName) -> Iterable[ModelFact]:
|
|
457
|
+
for fact in self.iterFacts(modelXbrl, qname):
|
|
458
|
+
if fact.xValid >= VALID:
|
|
459
|
+
yield fact
|
|
460
|
+
|
|
353
461
|
def iterValidNonNilFacts(self, modelXbrl: ModelXbrl, qname: QName) -> Iterable[ModelFact]:
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
if fact.xValid >= VALID and not fact.isNil:
|
|
462
|
+
for fact in self.iterValidFacts(modelXbrl, qname):
|
|
463
|
+
if not fact.isNil:
|
|
357
464
|
yield fact
|
|
358
465
|
|
|
359
466
|
def addUsedFilepath(self, modelXbrl: ModelXbrl, path: Path) -> None:
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎
|
|
2
|
+
×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,×,×,×,×,×,×,×,×,×,×,×,×
|
|
3
|
+
○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○
|
|
4
|
+
◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎
|
|
5
|
+
◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,△,△,△,△,△,△,△,△,△,△,△,△,△,△,△,△,△,△,△,△,△,△,△,△,△,△,△,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎
|
|
6
|
+
×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,×,×,×,×,×,×,×,×,×,×,×,×
|
|
7
|
+
×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,×,×,×,×,×,×,×,×,×,×,×,×
|
|
8
|
+
◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎
|
|
9
|
+
◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,×,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎
|
|
10
|
+
○,×,×,○,○,○,○,◎,◎,◎,◎,◎,◎,×,◎,◎,◎,◎,◎,◎,×,×,×,×,×,×,○,○,×,×,○,○,○,○,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×
|
|
11
|
+
○,×,×,○,○,○,○,◎,◎,◎,◎,◎,◎,×,◎,◎,◎,◎,◎,◎,×,×,×,×,×,×,○,○,×,×,○,○,○,○,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×
|
|
12
|
+
○,×,×,○,○,○,○,○,○,○,○,○,○,×,○,○,○,○,○,○,×,×,×,×,×,×,○,○,×,×,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×
|
|
13
|
+
○,×,×,○,○,○,○,◎,◎,◎,○,◎,◎,×,◎,◎,◎,◎,○,◎,×,×,×,×,×,×,○,○,×,×,○,○,○,○,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×
|
|
14
|
+
○,×,×,○,○,○,○,◎,◎,◎,◎,◎,◎,×,◎,◎,◎,◎,◎,◎,×,×,×,×,×,×,○,○,×,×,○,○,○,○,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×
|
|
15
|
+
○,×,×,○,○,○,○,◎,◎,◎,◎,◎,◎,×,◎,◎,◎,◎,◎,◎,×,×,×,×,×,×,○,○,×,×,○,○,○,○,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×
|
|
16
|
+
○,×,×,○,○,○,○,◎,◎,◎,◎,◎,◎,×,◎,◎,◎,◎,◎,◎,×,×,×,×,×,×,○,○,×,×,○,○,○,○,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×
|
|
17
|
+
○,×,×,○,○,○,○,◎,◎,◎,◎,◎,◎,×,◎,◎,◎,◎,◎,◎,×,×,×,×,×,×,○,○,×,×,○,○,○,○,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×
|
|
18
|
+
○,×,×,○,○,○,○,○,○,○,○,○,○,×,○,○,○,○,○,○,×,×,×,×,×,×,○,○,×,×,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×
|
|
19
|
+
○,×,×,○,○,○,○,○,○,○,○,○,○,×,○,○,○,○,○,○,×,×,×,×,×,×,○,○,×,×,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×
|
|
20
|
+
○,×,×,○,○,○,○,○,○,○,○,○,○,×,○,○,○,○,○,○,×,×,×,×,×,×,○,○,×,×,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×
|
|
21
|
+
○,×,×,○,○,○,○,×,×,×,×,×,×,×,○,○,×,×,×,×,×,×,×,×,×,×,○,○,×,×,○,○,○,○,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×
|
|
22
|
+
○,×,×,○,○,○,○,×,×,×,×,×,×,×,○,○,×,×,×,×,×,×,×,×,×,×,○,○,×,×,○,○,○,○,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×,×
|
|
23
|
+
◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎
|
|
24
|
+
◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎
|
|
25
|
+
○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○,○
|
|
26
|
+
◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎
|
|
27
|
+
◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎,◎
|
|
@@ -93,7 +93,7 @@ def rule_EC1057E(
|
|
|
93
93
|
Ensure that there is a nonnil value disclosed for FilingDateCoverPage
|
|
94
94
|
Note: This rule is only applicable to the public documents.
|
|
95
95
|
"""
|
|
96
|
-
dei = pluginData.
|
|
96
|
+
dei = pluginData.getFormTypes(val.modelXbrl)
|
|
97
97
|
if len(dei) > 0:
|
|
98
98
|
if not (pluginData.hasValidNonNilFact(val.modelXbrl, pluginData.jpcrpEsrFilingDateCoverPageQn)
|
|
99
99
|
or pluginData.hasValidNonNilFact(val.modelXbrl, pluginData.jpcrpFilingDateCoverPageQn)
|
|
@@ -10,6 +10,7 @@ from typing import Any, cast, Iterable
|
|
|
10
10
|
import regex
|
|
11
11
|
|
|
12
12
|
from arelle import XbrlConst, XmlUtil
|
|
13
|
+
from arelle.ModelDtsObject import ModelConcept
|
|
13
14
|
from arelle.ModelInstanceObject import ModelFact
|
|
14
15
|
from arelle.ModelObject import ModelObject
|
|
15
16
|
from arelle.ModelValue import QName
|
|
@@ -32,6 +33,7 @@ from ..PluginValidationDataExtension import PluginValidationDataExtension
|
|
|
32
33
|
|
|
33
34
|
_: TypeGetText
|
|
34
35
|
|
|
36
|
+
DISALLOWED_LABEL_WHITE_SPACE_CHARACTERS = regex.compile(r'\s{2,}')
|
|
35
37
|
GFM_CONTEXT_DATE_PATTERN = regex.compile(r"^[12][0-9]{3}-[01][0-9]-[0-3][0-9]$")
|
|
36
38
|
GFM_RECOMMENDED_NAMESPACE_PREFIXES = {
|
|
37
39
|
XbrlConst.xbrli: ("xbrli",),
|
|
@@ -672,6 +674,117 @@ def rule_gfm_1_3_8(
|
|
|
672
674
|
)
|
|
673
675
|
|
|
674
676
|
|
|
677
|
+
@validation(
|
|
678
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
679
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
680
|
+
)
|
|
681
|
+
def rule_gfm_1_3_20(
|
|
682
|
+
pluginData: PluginValidationDataExtension,
|
|
683
|
+
val: ValidateXbrl,
|
|
684
|
+
*args: Any,
|
|
685
|
+
**kwargs: Any,
|
|
686
|
+
) -> Iterable[Validation]:
|
|
687
|
+
"""
|
|
688
|
+
EDINET.EC5700W: [GFM 1.3.20] Set the nillable attribute value to "true".
|
|
689
|
+
|
|
690
|
+
GFM 1.3.20 The nillable attribute value of an xsd:element must equal "true".
|
|
691
|
+
"""
|
|
692
|
+
nonNillableElements = set()
|
|
693
|
+
for concept in val.modelXbrl.qnameConcepts.values():
|
|
694
|
+
if concept.namespaceURI == XbrlConst.xsd:
|
|
695
|
+
if concept.get("nillable") == "false":
|
|
696
|
+
nonNillableElements.add(concept)
|
|
697
|
+
if len(nonNillableElements) > 0:
|
|
698
|
+
yield Validation.warning(
|
|
699
|
+
codes='EDINET.EC5700W.GFM.1.3.20',
|
|
700
|
+
msg=_("Set the nillable attribute value to 'true'."),
|
|
701
|
+
modelObject=nonNillableElements
|
|
702
|
+
)
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
@validation(
|
|
706
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
707
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
708
|
+
)
|
|
709
|
+
def rule_gfm_1_3_23(
|
|
710
|
+
pluginData: PluginValidationDataExtension,
|
|
711
|
+
val: ValidateXbrl,
|
|
712
|
+
*args: Any,
|
|
713
|
+
**kwargs: Any,
|
|
714
|
+
) -> Iterable[Validation]:
|
|
715
|
+
"""
|
|
716
|
+
EDINET.EC5700W: [GFM 1.3.23] Set the periodType attribute to "duration".
|
|
717
|
+
|
|
718
|
+
GFM 1.3.23 If the abstract attribute of xsd:element is "true", then the
|
|
719
|
+
xbrli:periodType attribute must be "duration".
|
|
720
|
+
"""
|
|
721
|
+
instantAbstractElements = set()
|
|
722
|
+
for concept in val.modelXbrl.qnameConcepts.values():
|
|
723
|
+
if concept.abstract == "true" and concept.periodType == "instant":
|
|
724
|
+
instantAbstractElements.add(concept)
|
|
725
|
+
if len(instantAbstractElements) > 0:
|
|
726
|
+
yield Validation.warning(
|
|
727
|
+
codes='EDINET.EC5700W.GFM.1.3.23',
|
|
728
|
+
msg=_("Set the periodType attribute to 'duration'."),
|
|
729
|
+
modelObject=instantAbstractElements
|
|
730
|
+
)
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
@validation(
|
|
734
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
735
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
736
|
+
)
|
|
737
|
+
def rule_gfm_1_3_30(
|
|
738
|
+
pluginData: PluginValidationDataExtension,
|
|
739
|
+
val: ValidateXbrl,
|
|
740
|
+
*args: Any,
|
|
741
|
+
**kwargs: Any,
|
|
742
|
+
) -> Iterable[Validation]:
|
|
743
|
+
"""
|
|
744
|
+
EDINET.EC5700W: [GFM 1.3.30] Set the periodType attribute to "duration".
|
|
745
|
+
|
|
746
|
+
GFM 1.3.30 If xsd:element type attribute equals "nonnum:domainItemType" then
|
|
747
|
+
the xbrli:periodType attribute must equal "duration".
|
|
748
|
+
"""
|
|
749
|
+
instantDomainElements = set()
|
|
750
|
+
for concept in val.modelXbrl.qnameConcepts.values():
|
|
751
|
+
if concept.type is not None and concept.type.isDomainItemType and concept.periodType == "instant":
|
|
752
|
+
instantDomainElements.add(concept)
|
|
753
|
+
if len(instantDomainElements) > 0:
|
|
754
|
+
yield Validation.warning(
|
|
755
|
+
codes='EDINET.EC5700W.GFM.1.3.30',
|
|
756
|
+
msg=_("Set the periodType attribute to 'duration'."),
|
|
757
|
+
modelObject=instantDomainElements
|
|
758
|
+
)
|
|
759
|
+
|
|
760
|
+
@validation(
|
|
761
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
762
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
763
|
+
)
|
|
764
|
+
def rule_gfm_1_3_31(
|
|
765
|
+
pluginData: PluginValidationDataExtension,
|
|
766
|
+
val: ValidateXbrl,
|
|
767
|
+
*args: Any,
|
|
768
|
+
**kwargs: Any,
|
|
769
|
+
) -> Iterable[Validation]:
|
|
770
|
+
"""
|
|
771
|
+
EDINET.EC5700W: [GFM 1.3.31] Set the abstract attribute to "true".
|
|
772
|
+
|
|
773
|
+
GFM 1.3.31: If xsd:element type attribute equals "nonnum:domainItemType" then
|
|
774
|
+
the abstract attribute must equal to "true".
|
|
775
|
+
"""
|
|
776
|
+
nonAbstractDomainElements = set()
|
|
777
|
+
for concept in val.modelXbrl.qnameConcepts.values():
|
|
778
|
+
if concept.type is not None and concept.type.isDomainItemType and concept.abstract != "true":
|
|
779
|
+
nonAbstractDomainElements.add(concept)
|
|
780
|
+
if len(nonAbstractDomainElements) > 0:
|
|
781
|
+
yield Validation.warning(
|
|
782
|
+
codes='EDINET.EC5700W.GFM.1.3.31',
|
|
783
|
+
msg=_("Set the abstract attribute to 'true'."),
|
|
784
|
+
modelObject=nonAbstractDomainElements
|
|
785
|
+
)
|
|
786
|
+
|
|
787
|
+
|
|
675
788
|
@validation(
|
|
676
789
|
hook=ValidationHook.XBRL_FINALLY,
|
|
677
790
|
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
@@ -683,7 +796,7 @@ def rule_gfm_1_5_6(
|
|
|
683
796
|
**kwargs: Any,
|
|
684
797
|
) -> Iterable[Validation]:
|
|
685
798
|
"""
|
|
686
|
-
EDINET.EC5700W: [GFM 1.5.6] The length of a label must be less than 511 characters unless
|
|
799
|
+
EDINET.EC5700W: [GFM 1.5.6] The length of a label must be less than 511 characters unless its role is documentation.
|
|
687
800
|
"""
|
|
688
801
|
labelRelationshipSet = val.modelXbrl.relationshipSet(XbrlConst.conceptLabel)
|
|
689
802
|
if labelRelationshipSet is None:
|
|
@@ -692,7 +805,7 @@ def rule_gfm_1_5_6(
|
|
|
692
805
|
labelRels = labelRelationshipSet.fromModelObject(concept)
|
|
693
806
|
for rel in labelRels:
|
|
694
807
|
label = rel.toModelObject
|
|
695
|
-
if label.role != XbrlConst.documentationLabel and label.viewText() is not None and len(label.viewText())
|
|
808
|
+
if label.role != XbrlConst.documentationLabel and label.viewText() is not None and len(label.viewText()) >= 511:
|
|
696
809
|
yield Validation.warning(
|
|
697
810
|
codes='EDINET.EC5700W.GFM.1.5.6',
|
|
698
811
|
msg=_("The concept of '%(concept)s' has a label classified as '%(role)s' that is longer than 511 characters: %(label)s"),
|
|
@@ -701,3 +814,75 @@ def rule_gfm_1_5_6(
|
|
|
701
814
|
label=label.viewText(),
|
|
702
815
|
modelObject=label
|
|
703
816
|
)
|
|
817
|
+
|
|
818
|
+
|
|
819
|
+
@validation(
|
|
820
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
821
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
822
|
+
)
|
|
823
|
+
def rule_gfm_1_5_7(
|
|
824
|
+
pluginData: PluginValidationDataExtension,
|
|
825
|
+
val: ValidateXbrl,
|
|
826
|
+
*args: Any,
|
|
827
|
+
**kwargs: Any,
|
|
828
|
+
) -> Iterable[Validation]:
|
|
829
|
+
"""
|
|
830
|
+
EDINET.EC5700W: [GFM 1.5.7] A label cannot contain the "<" character or consecutive white space characters including
|
|
831
|
+
but not limited to: space, carriage return, line feed or tab.
|
|
832
|
+
"""
|
|
833
|
+
labelRelationshipSet = val.modelXbrl.relationshipSet(XbrlConst.conceptLabel)
|
|
834
|
+
if labelRelationshipSet is None:
|
|
835
|
+
return
|
|
836
|
+
for concept in val.modelXbrl.qnameConcepts.values():
|
|
837
|
+
labelRels = labelRelationshipSet.fromModelObject(concept)
|
|
838
|
+
for rel in labelRels:
|
|
839
|
+
label = rel.toModelObject
|
|
840
|
+
if label.role != XbrlConst.documentationLabel and label.textValue is not None:
|
|
841
|
+
if '<' in label.textValue:
|
|
842
|
+
yield Validation.warning(
|
|
843
|
+
codes='EDINET.EC5700W.GFM.1.5.7',
|
|
844
|
+
msg=_("The concept of '%(concept)s' has a label classified as '%(role)s that contains the '<' character: %(label)s"),
|
|
845
|
+
concept=concept.qname,
|
|
846
|
+
role=label.role,
|
|
847
|
+
label=label.textValue,
|
|
848
|
+
modelObject=label
|
|
849
|
+
)
|
|
850
|
+
elif DISALLOWED_LABEL_WHITE_SPACE_CHARACTERS.search(label.textValue):
|
|
851
|
+
yield Validation.warning(
|
|
852
|
+
codes='EDINET.EC5700W.GFM.1.5.7',
|
|
853
|
+
msg=_("The concept of '%(concept)s' has a label classified as '%(role)s' that contains consecutive white space characters: %(label)s"),
|
|
854
|
+
concept=concept.qname,
|
|
855
|
+
role=label.role,
|
|
856
|
+
label=label.textValue,
|
|
857
|
+
modelObject=label
|
|
858
|
+
)
|
|
859
|
+
|
|
860
|
+
|
|
861
|
+
@validation(
|
|
862
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
863
|
+
disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
|
|
864
|
+
)
|
|
865
|
+
def rule_gfm_1_5_8(
|
|
866
|
+
pluginData: PluginValidationDataExtension,
|
|
867
|
+
val: ValidateXbrl,
|
|
868
|
+
*args: Any,
|
|
869
|
+
**kwargs: Any,
|
|
870
|
+
) -> Iterable[Validation]:
|
|
871
|
+
"""
|
|
872
|
+
EDINET.EC5700W: [GFM 1.5.8] A label should not begin or end with a white space character
|
|
873
|
+
"""
|
|
874
|
+
labelRelationshipSet = val.modelXbrl.relationshipSet(XbrlConst.conceptLabel)
|
|
875
|
+
if labelRelationshipSet is None:
|
|
876
|
+
return
|
|
877
|
+
for concept in val.modelXbrl.qnameConcepts.values():
|
|
878
|
+
labelRels = labelRelationshipSet.fromModelObject(concept)
|
|
879
|
+
for rel in labelRels:
|
|
880
|
+
label = rel.toModelObject
|
|
881
|
+
if label.textValue is not None and label.textValue != label.textValue.strip():
|
|
882
|
+
yield Validation.warning(
|
|
883
|
+
codes='EDINET.EC5700W.GFM.1.5.8',
|
|
884
|
+
msg=_("The concept of '%(concept)s' has a label that contains disallowed white space either at the begining or the end: '%(label)s'"),
|
|
885
|
+
concept=concept.qname,
|
|
886
|
+
label=label.textValue,
|
|
887
|
+
modelObject=label
|
|
888
|
+
)
|