arelle-release 2.37.48__py3-none-any.whl → 2.37.50__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/ModelDocument.py +16 -14
- arelle/ModelInstanceObject.py +1 -1
- arelle/ModelXbrl.py +21 -10
- arelle/WebCache.py +26 -16
- arelle/_version.py +2 -2
- arelle/api/Session.py +5 -2
- arelle/plugin/validate/DBA/PluginValidationDataExtension.py +0 -1
- arelle/plugin/validate/EDINET/Constants.py +15 -0
- arelle/plugin/validate/EDINET/PluginValidationDataExtension.py +145 -11
- arelle/plugin/validate/EDINET/Statement.py +139 -0
- arelle/plugin/validate/EDINET/rules/contexts.py +9 -12
- arelle/plugin/validate/EDINET/rules/edinet.py +105 -45
- arelle/plugin/validate/EDINET/rules/gfm.py +51 -6
- arelle/plugin/validate/ESEF/ESEF_2021/ValidateXbrlFinally.py +2 -2
- arelle/plugin/validate/ESEF/ESEF_Current/ValidateXbrlFinally.py +2 -2
- arelle/plugin/validate/NL/PluginValidationDataExtension.py +1 -1
- arelle/plugin/validate/NL/rules/fr_nl.py +6 -7
- arelle/plugin/validate/UK/ValidateUK.py +31 -66
- {arelle_release-2.37.48.dist-info → arelle_release-2.37.50.dist-info}/METADATA +20 -16
- {arelle_release-2.37.48.dist-info → arelle_release-2.37.50.dist-info}/RECORD +24 -23
- {arelle_release-2.37.48.dist-info → arelle_release-2.37.50.dist-info}/WHEEL +0 -0
- {arelle_release-2.37.48.dist-info → arelle_release-2.37.50.dist-info}/entry_points.txt +0 -0
- {arelle_release-2.37.48.dist-info → arelle_release-2.37.50.dist-info}/licenses/LICENSE.md +0 -0
- {arelle_release-2.37.48.dist-info → arelle_release-2.37.50.dist-info}/top_level.txt +0 -0
arelle/ModelDocument.py
CHANGED
|
@@ -1579,12 +1579,6 @@ def inlineIxdsDiscover(modelXbrl, modelIxdsDocument, setTargetModelXbrl=False, *
|
|
|
1579
1579
|
modelXbrl.targetRoleRefs = {} # roleRefs used by selected target
|
|
1580
1580
|
modelXbrl.targetArcroleRefs = {} # arcroleRefs used by selected target
|
|
1581
1581
|
modelXbrl.targetRelationships = set() # relationship elements used by selected target
|
|
1582
|
-
targetModelXbrl = modelXbrl if setTargetModelXbrl else None # modelXbrl of target for contexts/units in multi-target/multi-instance situation
|
|
1583
|
-
assignUnusedContextsUnits = (not setTargetModelXbrl and not ixdsTarget and
|
|
1584
|
-
not getattr(modelXbrl, "supplementalModelXbrls", ()) and (
|
|
1585
|
-
not getattr(modelXbrl, "targetIXDSesToLoad", ()) or
|
|
1586
|
-
set(e.modelDocument for e in modelXbrl.ixdsHtmlElements) ==
|
|
1587
|
-
set(x.modelDocument for e in getattr(modelXbrl, "targetIXDSesToLoad", ()) for x in e[1])))
|
|
1588
1582
|
hasResources = hasHeader = False
|
|
1589
1583
|
for htmlElement in modelXbrl.ixdsHtmlElements:
|
|
1590
1584
|
mdlDoc = htmlElement.modelDocument
|
|
@@ -1611,8 +1605,12 @@ def inlineIxdsDiscover(modelXbrl, modelIxdsDocument, setTargetModelXbrl=False, *
|
|
|
1611
1605
|
for modelInlineFact in htmlElement.iterdescendants(ixNStag + "nonNumeric", ixNStag + "nonFraction", ixNStag + "fraction"):
|
|
1612
1606
|
if isinstance(modelInlineFact,ModelObject):
|
|
1613
1607
|
_target = modelInlineFact.get("target")
|
|
1614
|
-
|
|
1615
|
-
|
|
1608
|
+
contextRef = modelInlineFact.get("contextRef")
|
|
1609
|
+
if contextRef is not None:
|
|
1610
|
+
factTargetContextRefs[_target].add(contextRef.strip())
|
|
1611
|
+
unitRef = modelInlineFact.get("unitRef")
|
|
1612
|
+
if unitRef is not None:
|
|
1613
|
+
factTargetUnitRefs[_target].add(unitRef.strip())
|
|
1616
1614
|
if modelInlineFact.id:
|
|
1617
1615
|
factsByFactID[modelInlineFact.id] = modelInlineFact
|
|
1618
1616
|
for elt in htmlElement.iterdescendants(tag=ixNStag + "continuation"):
|
|
@@ -1734,9 +1732,9 @@ def inlineIxdsDiscover(modelXbrl, modelIxdsDocument, setTargetModelXbrl=False, *
|
|
|
1734
1732
|
targetRoleUris[_target].add(footnoteRole)
|
|
1735
1733
|
|
|
1736
1734
|
contextRefs = factTargetContextRefs[ixdsTarget]
|
|
1735
|
+
contextRefsForAllTargets = {ref for refs in factTargetContextRefs.values() for ref in refs}
|
|
1737
1736
|
unitRefs = factTargetUnitRefs[ixdsTarget]
|
|
1738
|
-
|
|
1739
|
-
allUnitRefs = set.union(*factTargetUnitRefs.values())
|
|
1737
|
+
unitRefsForAllTargets = {ref for refs in factTargetUnitRefs.values() for ref in refs}
|
|
1740
1738
|
|
|
1741
1739
|
# discovery of contexts, units and roles which are used by target document
|
|
1742
1740
|
for htmlElement in modelXbrl.ixdsHtmlElements:
|
|
@@ -1745,13 +1743,17 @@ def inlineIxdsDiscover(modelXbrl, modelIxdsDocument, setTargetModelXbrl=False, *
|
|
|
1745
1743
|
|
|
1746
1744
|
for inlineElement in htmlElement.iterdescendants(tag=ixNStag + "resources"):
|
|
1747
1745
|
for elt in inlineElement.iterchildren("{http://www.xbrl.org/2003/instance}context"):
|
|
1748
|
-
|
|
1749
|
-
if
|
|
1746
|
+
contextId = elt.get("id")
|
|
1747
|
+
if contextId in contextRefs:
|
|
1750
1748
|
modelIxdsDocument.contextDiscover(elt, setTargetModelXbrl)
|
|
1749
|
+
elif contextId not in contextRefsForAllTargets:
|
|
1750
|
+
modelXbrl.ixdsUnmappedContexts[contextId] = elt
|
|
1751
1751
|
for elt in inlineElement.iterchildren("{http://www.xbrl.org/2003/instance}unit"):
|
|
1752
|
-
|
|
1753
|
-
if
|
|
1752
|
+
unitId = elt.get("id")
|
|
1753
|
+
if unitId in unitRefs:
|
|
1754
1754
|
modelIxdsDocument.unitDiscover(elt, setTargetModelXbrl)
|
|
1755
|
+
elif unitId not in unitRefsForAllTargets:
|
|
1756
|
+
modelXbrl.ixdsUnmappedUnits[unitId] = elt
|
|
1755
1757
|
for refElement in inlineElement.iterchildren("{http://www.xbrl.org/2003/linkbase}roleRef"):
|
|
1756
1758
|
r = refElement.get("roleURI")
|
|
1757
1759
|
if r in targetRoleUris[ixdsTarget]:
|
arelle/ModelInstanceObject.py
CHANGED
|
@@ -1159,7 +1159,7 @@ class ModelContext(ModelObject):
|
|
|
1159
1159
|
self._dimsHash = hash( frozenset(self.qnameDims.values()) )
|
|
1160
1160
|
return self._dimsHash
|
|
1161
1161
|
|
|
1162
|
-
def nonDimValues(self, contextElement):
|
|
1162
|
+
def nonDimValues(self, contextElement: str | int) -> list[ModelObject]:
|
|
1163
1163
|
"""([ModelObject]) -- ContextElement is either string or Aspect code for segment or scenario, returns nonXDT ModelObject children of context element.
|
|
1164
1164
|
|
|
1165
1165
|
:param contextElement: one of 'segment', 'scenario', Aspect.NON_XDT_SEGMENT, Aspect.NON_XDT_SCENARIO, Aspect.COMPLETE_SEGMENT, Aspect.COMPLETE_SCENARIO
|
arelle/ModelXbrl.py
CHANGED
|
@@ -12,7 +12,7 @@ from collections import defaultdict
|
|
|
12
12
|
from typing import TYPE_CHECKING, Any, TypeVar, Union, cast, Optional
|
|
13
13
|
|
|
14
14
|
import regex as re
|
|
15
|
-
from collections.abc import Iterable
|
|
15
|
+
from collections.abc import Iterable, Iterator
|
|
16
16
|
|
|
17
17
|
import arelle
|
|
18
18
|
from arelle import FileSource, ModelRelationshipSet, XmlUtil, ModelValue, XbrlConst, XmlValidate
|
|
@@ -335,8 +335,12 @@ class ModelXbrl:
|
|
|
335
335
|
self.facts: list[ModelFact] = []
|
|
336
336
|
self.factsInInstance: set[ModelFact] = set()
|
|
337
337
|
self.undefinedFacts: list[ModelFact] = [] # elements presumed to be facts but not defined
|
|
338
|
-
self.contexts: dict[str,
|
|
338
|
+
self.contexts: dict[str, ModelContext] = {}
|
|
339
|
+
self.ixdsUnmappedContexts: dict[str, ModelContext] = {}
|
|
340
|
+
self._contextsInUseMarked = False
|
|
339
341
|
self.units: dict[str, ModelUnit] = {}
|
|
342
|
+
self.ixdsUnmappedUnits: dict[str, ModelUnit] = {}
|
|
343
|
+
self._unitsInUseMarked = False
|
|
340
344
|
self.modelObjects: list[ModelObject] = []
|
|
341
345
|
self.qnameParameters: dict[QName, Any] = {}
|
|
342
346
|
self.modelVariableSets: set[ModelVariableSet] = set()
|
|
@@ -604,7 +608,7 @@ class ModelXbrl:
|
|
|
604
608
|
for cOCCs,mOCCs in ((c.nonDimValues(segAspect),segOCCs),
|
|
605
609
|
(c.nonDimValues(scenAspect),scenOCCs)))
|
|
606
610
|
):
|
|
607
|
-
return
|
|
611
|
+
return c
|
|
608
612
|
return None
|
|
609
613
|
|
|
610
614
|
def createContext(
|
|
@@ -868,17 +872,24 @@ class ModelXbrl:
|
|
|
868
872
|
return fbdq[memQname]
|
|
869
873
|
|
|
870
874
|
@property
|
|
871
|
-
def contextsInUse(self) ->
|
|
872
|
-
|
|
873
|
-
if self._contextsInUseMarked:
|
|
874
|
-
return (cntx for cntx in self.contexts.values() if getattr(cntx, "_inUse", False))
|
|
875
|
-
except AttributeError:
|
|
875
|
+
def contextsInUse(self) -> Iterator[ModelContext]:
|
|
876
|
+
if not self._contextsInUseMarked:
|
|
876
877
|
for fact in self.factsInInstance:
|
|
877
878
|
cntx = fact.context
|
|
878
879
|
if cntx is not None:
|
|
879
880
|
cntx._inUse = True
|
|
880
|
-
self._contextsInUseMarked
|
|
881
|
-
|
|
881
|
+
self._contextsInUseMarked = True
|
|
882
|
+
return (cntx for cntx in self.contexts.values() if getattr(cntx, "_inUse", False))
|
|
883
|
+
|
|
884
|
+
@property
|
|
885
|
+
def unitsInUse(self) -> Iterator[ModelUnit]:
|
|
886
|
+
if not self._unitsInUseMarked:
|
|
887
|
+
for fact in self.factsInInstance:
|
|
888
|
+
unit = fact.unit
|
|
889
|
+
if unit is not None:
|
|
890
|
+
unit._inUse = True
|
|
891
|
+
self._unitsInUseMarked = True
|
|
892
|
+
return (unit for unit in self.units.values() if getattr(unit, "_inUse", False))
|
|
882
893
|
|
|
883
894
|
@property
|
|
884
895
|
def dimensionsInUse(self) -> set[Any]:
|
arelle/WebCache.py
CHANGED
|
@@ -6,20 +6,28 @@ e.g., User-Agent: Sample Company Name AdminContact@<sample company domain>.com
|
|
|
6
6
|
|
|
7
7
|
'''
|
|
8
8
|
from __future__ import annotations
|
|
9
|
-
import contextlib
|
|
10
|
-
|
|
11
9
|
|
|
12
|
-
|
|
10
|
+
import calendar
|
|
11
|
+
import contextlib
|
|
12
|
+
import io
|
|
13
|
+
import json
|
|
14
|
+
import logging
|
|
15
|
+
import os
|
|
16
|
+
import posixpath
|
|
17
|
+
import shutil
|
|
18
|
+
import sys
|
|
19
|
+
import time
|
|
20
|
+
import zlib
|
|
21
|
+
from http.client import IncompleteRead
|
|
13
22
|
from pathlib import Path
|
|
14
23
|
from typing import TYPE_CHECKING, Any
|
|
15
|
-
import os, posixpath, sys, time, calendar, io, json, logging, shutil, zlib
|
|
16
|
-
import regex as re
|
|
17
|
-
from urllib.parse import quote, unquote
|
|
18
|
-
from urllib.error import URLError, HTTPError, ContentTooShortError
|
|
19
|
-
from http.client import IncompleteRead
|
|
20
24
|
from urllib import request as proxyhandlers
|
|
25
|
+
from urllib.error import ContentTooShortError, HTTPError, URLError
|
|
26
|
+
from urllib.parse import quote, unquote, urlsplit, urlunsplit
|
|
21
27
|
|
|
22
28
|
import certifi
|
|
29
|
+
import regex as re
|
|
30
|
+
from filelock import FileLock, Timeout
|
|
23
31
|
|
|
24
32
|
from arelle.PythonUtil import isLegacyAbs
|
|
25
33
|
|
|
@@ -567,14 +575,16 @@ class WebCache:
|
|
|
567
575
|
:param url:
|
|
568
576
|
:return: `url` with scheme-specific-part quoted except for parameter separators
|
|
569
577
|
"""
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
# RFC 3986: https://www.ietf.org/rfc/rfc3986.txt
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
+
parts = urlsplit(url)
|
|
579
|
+
|
|
580
|
+
# RFC 3986 safe characters: https://www.ietf.org/rfc/rfc3986.txt
|
|
581
|
+
pathSafe = "/:@!$&'()*+,;=" # path allows sub-delims, ":" and "@"
|
|
582
|
+
querySafe = "&=:/?@!$'()*+,;[]" # query allows pchar + "/" + "?"
|
|
583
|
+
|
|
584
|
+
quotedPath = quote(parts.path, safe=pathSafe)
|
|
585
|
+
quotedQuery = quote(parts.query, safe=querySafe)
|
|
586
|
+
|
|
587
|
+
return urlunsplit((parts.scheme, parts.netloc, quotedPath, quotedQuery, parts.fragment))
|
|
578
588
|
|
|
579
589
|
@staticmethod
|
|
580
590
|
def _getFileTimestamp(path: str) -> float:
|
arelle/_version.py
CHANGED
arelle/api/Session.py
CHANGED
|
@@ -8,7 +8,7 @@ from __future__ import annotations
|
|
|
8
8
|
import logging
|
|
9
9
|
import threading
|
|
10
10
|
from types import TracebackType
|
|
11
|
-
from typing import Any, BinaryIO
|
|
11
|
+
from typing import Any, BinaryIO, TypeVar
|
|
12
12
|
|
|
13
13
|
from arelle import PackageManager, PluginManager
|
|
14
14
|
from arelle.CntlrCmdLine import CntlrCmdLine, createCntlrAndPreloadPlugins
|
|
@@ -18,6 +18,9 @@ from arelle.RuntimeOptions import RuntimeOptions
|
|
|
18
18
|
|
|
19
19
|
_session_lock = threading.Lock()
|
|
20
20
|
|
|
21
|
+
# typing.Self can be used once Python 3.10 support is dropped.
|
|
22
|
+
Self = TypeVar("Self", bound="Session")
|
|
23
|
+
|
|
21
24
|
|
|
22
25
|
class Session:
|
|
23
26
|
"""
|
|
@@ -46,7 +49,7 @@ class Session:
|
|
|
46
49
|
"Session objects cannot be shared between threads. Create a new Session instance in each thread."
|
|
47
50
|
)
|
|
48
51
|
|
|
49
|
-
def __enter__(self) ->
|
|
52
|
+
def __enter__(self: Self) -> Self:
|
|
50
53
|
return self
|
|
51
54
|
|
|
52
55
|
def __exit__(
|
|
@@ -146,7 +146,6 @@ class PluginValidationDataExtension(PluginData):
|
|
|
146
146
|
return self._reportingPeriodContexts
|
|
147
147
|
contexts = []
|
|
148
148
|
for context in modelXbrl.contexts.values():
|
|
149
|
-
context = cast(ModelContext, context)
|
|
150
149
|
if context.isInstantPeriod or context.isForeverPeriod:
|
|
151
150
|
continue # Reporting period contexts can't be instant/forever contexts
|
|
152
151
|
if len(context.qnameDims) > 0:
|
|
@@ -5,16 +5,30 @@ 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 = '第二号の七様式'
|
|
11
16
|
FORM_3 = '第三号様式'
|
|
17
|
+
FORM_4 = '第四号様式'
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def isStockReport(self) -> bool:
|
|
21
|
+
return self in STOCK_REPORT_FORMS
|
|
12
22
|
|
|
13
23
|
CORPORATE_FORMS =frozenset([
|
|
14
24
|
FormType.FORM_2_4,
|
|
15
25
|
FormType.FORM_2_7,
|
|
16
26
|
FormType.FORM_3,
|
|
17
27
|
])
|
|
28
|
+
STOCK_REPORT_FORMS = frozenset([
|
|
29
|
+
FormType.FORM_3,
|
|
30
|
+
FormType.FORM_4,
|
|
31
|
+
])
|
|
18
32
|
qnEdinetManifestInsert = qname("{http://disclosure.edinet-fsa.go.jp/2013/manifest}insert")
|
|
19
33
|
qnEdinetManifestInstance = qname("{http://disclosure.edinet-fsa.go.jp/2013/manifest}instance")
|
|
20
34
|
qnEdinetManifestItem = qname("{http://disclosure.edinet-fsa.go.jp/2013/manifest}item")
|
|
@@ -22,3 +36,4 @@ qnEdinetManifestIxbrl = qname("{http://disclosure.edinet-fsa.go.jp/2013/manifest
|
|
|
22
36
|
qnEdinetManifestList = qname("{http://disclosure.edinet-fsa.go.jp/2013/manifest}list")
|
|
23
37
|
qnEdinetManifestTitle = qname("{http://disclosure.edinet-fsa.go.jp/2013/manifest}title")
|
|
24
38
|
qnEdinetManifestTocComposition = qname("{http://disclosure.edinet-fsa.go.jp/2013/manifest}tocComposition")
|
|
39
|
+
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
|
|
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
|
|
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.
|
|
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
|
|
@@ -20,21 +24,26 @@ from arelle.ValidateDuplicateFacts import getDeduplicatedFacts, DeduplicationTyp
|
|
|
20
24
|
from arelle.XmlValidate import VALID
|
|
21
25
|
from arelle.typing import TypeGetText
|
|
22
26
|
from arelle.utils.PluginData import PluginData
|
|
23
|
-
from .Constants import CORPORATE_FORMS
|
|
27
|
+
from .Constants import CORPORATE_FORMS, FormType
|
|
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
|
|
|
@@ -45,19 +54,21 @@ class PluginValidationDataExtension(PluginData):
|
|
|
45
54
|
def __init__(self, name: str):
|
|
46
55
|
super().__init__(name)
|
|
47
56
|
jpcrpEsrNamespace = "http://disclosure.edinet-fsa.go.jp/taxonomy/jpcrp-esr/2024-11-01/jpcrp-esr_cor"
|
|
48
|
-
jpcrpNamespace = 'http://disclosure.edinet-fsa.go.jp/taxonomy/jpcrp/2024-11-01/jpcrp_cor'
|
|
57
|
+
self.jpcrpNamespace = 'http://disclosure.edinet-fsa.go.jp/taxonomy/jpcrp/2024-11-01/jpcrp_cor'
|
|
49
58
|
jpdeiNamespace = 'http://disclosure.edinet-fsa.go.jp/taxonomy/jpdei/2013-08-31/jpdei_cor'
|
|
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')
|
|
66
|
+
self.issuedSharesTotalNumberOfSharesEtcQn = qname(self.jpcrpNamespace, 'IssuedSharesTotalNumberOfSharesEtcTextBlock')
|
|
55
67
|
self.jpcrpEsrFilingDateCoverPageQn = qname(jpcrpEsrNamespace, 'FilingDateCoverPage')
|
|
56
|
-
self.jpcrpFilingDateCoverPageQn = qname(jpcrpNamespace, 'FilingDateCoverPage')
|
|
68
|
+
self.jpcrpFilingDateCoverPageQn = qname(self.jpcrpNamespace, 'FilingDateCoverPage')
|
|
57
69
|
self.jpspsFilingDateCoverPageQn = qname(jpspsNamespace, 'FilingDateCoverPage')
|
|
58
|
-
self.liabilitiesAndEquityIfrsQn = qname(jpigpNamespace, "LiabilitiesAndEquityIFRS")
|
|
59
70
|
self.nonConsolidatedMemberQn = qname(jppfsNamespace, "NonConsolidatedMember")
|
|
60
|
-
self.ratioOfFemaleDirectorsAndOtherOfficersQn = qname(jpcrpNamespace, "RatioOfFemaleDirectorsAndOtherOfficers")
|
|
71
|
+
self.ratioOfFemaleDirectorsAndOtherOfficersQn = qname(self.jpcrpNamespace, "RatioOfFemaleDirectorsAndOtherOfficers")
|
|
61
72
|
|
|
62
73
|
self.contextIdPattern = regex.compile(r'(Prior[1-9]Year|CurrentYear|Prior[1-9]Interim|Interim)(Duration|Instant)')
|
|
63
74
|
|
|
@@ -65,6 +76,27 @@ class PluginValidationDataExtension(PluginData):
|
|
|
65
76
|
def __hash__(self) -> int:
|
|
66
77
|
return id(self)
|
|
67
78
|
|
|
79
|
+
@lru_cache(1)
|
|
80
|
+
def _contextMatchesStatement(self, modelXbrl: ModelXbrl, contextId: str, statement: Statement) -> bool:
|
|
81
|
+
"""
|
|
82
|
+
:return: Whether the context's facts are applicable to the given statement.
|
|
83
|
+
"""
|
|
84
|
+
if 'Interim' in contextId:
|
|
85
|
+
# valid06.zip suggests "interim"" contexts are not considered for balance sheets.
|
|
86
|
+
return False
|
|
87
|
+
context = modelXbrl.contexts[contextId]
|
|
88
|
+
if not all(dimQn == self.consolidatedOrNonConsolidatedAxisQn for dimQn in context.qnameDims):
|
|
89
|
+
return False
|
|
90
|
+
memberValue = context.dimMemberQname(self.consolidatedOrNonConsolidatedAxisQn, includeDefaults=True)
|
|
91
|
+
contextIsConsolidated = memberValue != self.nonConsolidatedMemberQn
|
|
92
|
+
return bool(statement.isConsolidated == contextIsConsolidated)
|
|
93
|
+
|
|
94
|
+
def _isDebitConcept(self, concept: ModelConcept) -> bool:
|
|
95
|
+
"""
|
|
96
|
+
:return: Whether the given concept is a debit concept.
|
|
97
|
+
"""
|
|
98
|
+
return bool(_DEBIT_QNAME_PATTERN.match(concept.qname.localName))
|
|
99
|
+
|
|
68
100
|
@lru_cache(1)
|
|
69
101
|
def isCorporateForm(self, modelXbrl: ModelXbrl) -> bool:
|
|
70
102
|
documentTypes = self.getDocumentTypes(modelXbrl)
|
|
@@ -72,6 +104,85 @@ class PluginValidationDataExtension(PluginData):
|
|
|
72
104
|
return True
|
|
73
105
|
return False
|
|
74
106
|
|
|
107
|
+
def isCorporateReport(self, modelXbrl: ModelXbrl) -> bool:
|
|
108
|
+
return self.jpcrpNamespace in modelXbrl.namespaceDocs
|
|
109
|
+
|
|
110
|
+
def isStockForm(self, modelXbrl: ModelXbrl) -> bool:
|
|
111
|
+
documentTypes = self.getDocumentTypes(modelXbrl)
|
|
112
|
+
return any(documentType == form.value for form in FormType if form.isStockReport for documentType in documentTypes)
|
|
113
|
+
|
|
114
|
+
def getBalanceSheets(self, modelXbrl: ModelXbrl, statement: Statement) -> list[BalanceSheet]:
|
|
115
|
+
"""
|
|
116
|
+
:return: Balance sheet data for each context/unit pairing the given statement.
|
|
117
|
+
"""
|
|
118
|
+
balanceSheets: list[BalanceSheet] = []
|
|
119
|
+
if statement.roleUri not in modelXbrl.roleTypes:
|
|
120
|
+
return balanceSheets
|
|
121
|
+
if statement.statementType not in (
|
|
122
|
+
StatementType.BALANCE_SHEET,
|
|
123
|
+
StatementType.STATEMENT_OF_FINANCIAL_POSITION
|
|
124
|
+
):
|
|
125
|
+
return balanceSheets
|
|
126
|
+
|
|
127
|
+
relSet = modelXbrl.relationshipSet(
|
|
128
|
+
tuple(LinkbaseType.CALCULATION.getArcroles()),
|
|
129
|
+
linkrole=statement.roleUri
|
|
130
|
+
)
|
|
131
|
+
rootConcepts = relSet.rootConcepts
|
|
132
|
+
if len(rootConcepts) == 0:
|
|
133
|
+
return balanceSheets
|
|
134
|
+
|
|
135
|
+
# GFM 1.2.7 and 1.2.10 asserts no duplicate contexts and units, respectively,
|
|
136
|
+
# so context and unit IDs can be used as a key.
|
|
137
|
+
factsByContextIdAndUnitId = self.getFactsByContextAndUnit(
|
|
138
|
+
modelXbrl,
|
|
139
|
+
attrgetter("id"),
|
|
140
|
+
attrgetter("id"),
|
|
141
|
+
tuple(concept.qname for concept in rootConcepts)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
for (contextId, unitId), facts in factsByContextIdAndUnitId.items():
|
|
145
|
+
if not self._contextMatchesStatement(modelXbrl, contextId, statement):
|
|
146
|
+
continue
|
|
147
|
+
assetSum = Decimal(0)
|
|
148
|
+
liabilitiesAndEquitySum = Decimal(0)
|
|
149
|
+
for fact in facts:
|
|
150
|
+
if isinstance(fact.xValue, float):
|
|
151
|
+
value = Decimal(fact.xValue)
|
|
152
|
+
else:
|
|
153
|
+
value = cast(Decimal, fact.xValue)
|
|
154
|
+
if self._isDebitConcept(fact.concept):
|
|
155
|
+
liabilitiesAndEquitySum += value
|
|
156
|
+
else:
|
|
157
|
+
assetSum += value
|
|
158
|
+
balanceSheets.append(
|
|
159
|
+
BalanceSheet(
|
|
160
|
+
assetsTotal=assetSum,
|
|
161
|
+
contextId=str(contextId),
|
|
162
|
+
facts=facts,
|
|
163
|
+
liabilitiesAndEquityTotal=liabilitiesAndEquitySum,
|
|
164
|
+
unitId=str(unitId),
|
|
165
|
+
)
|
|
166
|
+
)
|
|
167
|
+
return balanceSheets
|
|
168
|
+
|
|
169
|
+
@lru_cache(1)
|
|
170
|
+
def getStatementInstance(self, modelXbrl: ModelXbrl, statement: Statement) -> StatementInstance | None:
|
|
171
|
+
if statement.roleUri not in modelXbrl.roleTypes:
|
|
172
|
+
return None
|
|
173
|
+
return StatementInstance(
|
|
174
|
+
balanceSheets=self.getBalanceSheets(modelXbrl, statement),
|
|
175
|
+
statement=statement,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
@lru_cache(1)
|
|
179
|
+
def getStatementInstances(self, modelXbrl: ModelXbrl) -> list[StatementInstance]:
|
|
180
|
+
return [
|
|
181
|
+
statementInstance
|
|
182
|
+
for statement in STATEMENTS
|
|
183
|
+
if (statementInstance := self.getStatementInstance(modelXbrl, statement)) is not None
|
|
184
|
+
]
|
|
185
|
+
|
|
75
186
|
@lru_cache(1)
|
|
76
187
|
def getDeduplicatedFacts(self, modelXbrl: ModelXbrl) -> list[ModelFact]:
|
|
77
188
|
return getDeduplicatedFacts(modelXbrl, DeduplicationType.CONSISTENT_PAIRS)
|
|
@@ -85,6 +196,24 @@ class PluginValidationDataExtension(PluginData):
|
|
|
85
196
|
documentTypes.add(fact.textValue)
|
|
86
197
|
return documentTypes
|
|
87
198
|
|
|
199
|
+
def getFactsByContextAndUnit(
|
|
200
|
+
self, modelXbrl: ModelXbrl,
|
|
201
|
+
getContextKey: Callable[[ModelContext], Hashable],
|
|
202
|
+
getUnitKey: Callable[[ModelUnit], Hashable],
|
|
203
|
+
qnames: tuple[QName, ...] | None = None,
|
|
204
|
+
) -> dict[tuple[Hashable, Hashable], list[ModelFact]]:
|
|
205
|
+
deduplicatedFacts = self.getDeduplicatedFacts(modelXbrl)
|
|
206
|
+
getFactsByContextAndUnit = defaultdict(list)
|
|
207
|
+
for fact in deduplicatedFacts:
|
|
208
|
+
if qnames is not None and fact.qname not in qnames:
|
|
209
|
+
continue
|
|
210
|
+
if fact.context is None or fact.unit is None:
|
|
211
|
+
continue
|
|
212
|
+
contextKey = getContextKey(fact.context)
|
|
213
|
+
unitKey = getUnitKey(fact.unit)
|
|
214
|
+
getFactsByContextAndUnit[(contextKey, unitKey)].append(fact)
|
|
215
|
+
return dict(getFactsByContextAndUnit)
|
|
216
|
+
|
|
88
217
|
@lru_cache(1)
|
|
89
218
|
def getFootnoteLinkElements(self, modelXbrl: ModelXbrl) -> list[ModelObject | LinkPrototype]:
|
|
90
219
|
# TODO: Consolidate with similar implementations in EDGAR and FERC
|
|
@@ -113,8 +242,13 @@ class PluginValidationDataExtension(PluginData):
|
|
|
113
242
|
return controllerPluginData.matchManifestInstance(modelXbrl.ixdsDocUrls)
|
|
114
243
|
|
|
115
244
|
def hasValidNonNilFact(self, modelXbrl: ModelXbrl, qname: QName) -> bool:
|
|
116
|
-
|
|
117
|
-
return any(fact.xValid >= VALID and not fact.isNil for fact in requiredFacts)
|
|
245
|
+
return any(fact is not None for fact in self.iterValidNonNilFacts(modelXbrl, qname))
|
|
118
246
|
|
|
119
247
|
def isStandardTaxonomyUrl(self, uri: str, modelXbrl: ModelXbrl) -> bool:
|
|
120
248
|
return modelXbrl.modelManager.disclosureSystem.hrefValidForDisclosureSystem(uri)
|
|
249
|
+
|
|
250
|
+
def iterValidNonNilFacts(self, modelXbrl: ModelXbrl, qname: QName) -> Iterable[ModelFact]:
|
|
251
|
+
facts = modelXbrl.factsByQname.get(qname, set())
|
|
252
|
+
for fact in facts:
|
|
253
|
+
if fact.xValid >= VALID and not fact.isNil:
|
|
254
|
+
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()
|