arelle-release 2.37.58__py3-none-any.whl → 2.37.60__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/LeiUtil.py CHANGED
@@ -1,55 +1,75 @@
1
- '''
1
+ """
2
2
  See COPYRIGHT.md for copyright information.
3
3
 
4
4
  Implementation of ISO 17442:2012(E) Appendix A
5
5
 
6
- '''
6
+ """
7
+
8
+ import string
9
+ from enum import Enum, auto
10
+
7
11
  import regex as re
8
12
 
9
- LEI_VALID = 0
10
- LEI_INVALID_LEXICAL = 1
11
- LEI_INVALID_CHECKSUM = 2
12
13
 
13
- LEI_RESULTS = ("valid", "invalid lexical", "invalid checksum")
14
+ class LEIValidationResult(Enum):
15
+ VALID = auto()
16
+ INVALID_LEXICAL = auto()
17
+ INVALID_CHECKSUM = auto()
18
+
19
+ def description(self) -> str:
20
+ return self.name.lower().replace("_", " ")
21
+
22
+
23
+ LEI_VALID = LEIValidationResult.VALID
24
+ LEI_INVALID_LEXICAL = LEIValidationResult.INVALID_LEXICAL
25
+ LEI_INVALID_CHECKSUM = LEIValidationResult.INVALID_CHECKSUM
26
+
27
+ _leiLexicalPattern = re.compile(r"[0-9A-Z]{18}[0-9]{2}", re.ASCII)
28
+ _requiredLEILength = 20
29
+
30
+ _leiToDigitTable = str.maketrans(
31
+ dict(
32
+ (ord(char), str(index))
33
+ for index, char in enumerate(string.ascii_uppercase, start=len(string.digits))
34
+ )
35
+ )
36
+
37
+ # The pattern comes from Solvency II documentation here https://dev.eiopa.europa.eu/Taxonomy/Full/2.3.0/PF/EIOPA_PensionFunds_Validations_2.3.0_PWD2.xlsx
38
+ _validLeiDespiteChecksumFailPatternString = r"^(029200720E3M3A4D6D01|029200758D5M0AI3F601|315700X8JQ3IU0NGK501|3157007SCCESQAUH5Z01|315700TCC9NTEP7J8Z01|13250000000000000301|315700ADOXDR5PCY5400|315700A0UB9Q7DOQIZ00|315700BM9Z39TNTGQW00|315700BZ5F7DRYG2UM00|315700B8401GTFFY6X01|315700DJ07P6OX10FK01|315700DKCD4QSKLAMO01|315700D23JL5C1DZNT00|315700EIYO2TLSEGQ700|315700ET7M7VQ4C84R00|315700EZSEA51937KX01|315700GXBGM8DBYKHF01|315700GZA843JXKTJ400|315700G5G24XYL1TXH00|315700HS7WJ1B0SUWM01|315700HXOEOK58E58P01|315700HZU4SMI8LZTU00|315700I3W2AFHP8MNQ01|315700JICZ3SY5SAXX00|315700JKZH6I0067ND01|315700JO5E28SRE00Q01|315700JXUHL9H2C3P700|315700LDDN3RM7Y2MP00|315700LWYOZNQ7V1T100|315700MBYPT6PGKO7M01|315700MW2F0KFR45QW01|315700M5843O6DU83901|315700NNMGS8F3P2CN00|315700N0VEIBHP0NPQ01|315700OASRCM664PAW01|315700OFW4YCOBNX4U01|315700OVW93X0T3HP200|315700O666JVNCQU9X00|315700PLI0I7W8IOV400|315700PN3J57ZUNF1V00|315700PXKOSX7WQV4N00|315700P4N9VSLK5QZV01|315700P40OV6BT045900|315700P6TZOLP92KN801|315700P89WR82VNB8Z00|315700QGM4XWZE1I5N01|315700Q1S8O1UORF9700|315700RK8M4FAHMYAP01|315700RTEHY362KXWJ00|315700SNUXK41WMW5J00|315700S22RGYRIEEOT00|315700S3TF79ALV82F01|315700TF5Z7T28HZJK01|315700TWGZ89LLSRS000|315700TXNX10N8XH4K00|315700T2EEQAPBO0C301|315700T6T49EDM16YO01|315700UJ6N4LGKLNPB00|315700UKZXWXEO126601|315700UYFD5GF9R13F01|315700VG7PTE9EJJRX01|315700VITYR7AL4M9S01|315700VMAJZ9JZTXNQ00|315700WH3YMKHCVYW201|315700WKCDF4QGRRO200|315700WR4IHOO1M5LP00|315700WYOZ6994UATN00|315700WZPEIS41QDKE00|315700XEFYMA5EZ0P500|315700XE21UYOA3GAC01|315700XI4Z8GF5BDUJ01|315700XSCP1S8WOD8E01|315700X40GNCOUWJYR00|315700YS6RQ5TF3VBP01|315700Y1W7W1JHAUBW00|315700Y5JNQMMUF5ID01|3157000VAJWZ3P8ZED00|3157001KM8GOU7PXZY01|3157001K2LAL04D87901|3157001MLDD3SDFQA901|3157001TPR6K4GBTLN00|31570010000000006400|31570010000000009601|31570010000000019301|31570010000000025800|31570010000000029001|31570010000000035500|31570010000000038701|31570010000000045200|31570010000000048401|31570010000000054900|31570010000000064600|31570010000000067801|31570010000000077501|31570010000000084000|31570010000000087201|31570010000000096901|31570010000000103400|31570010000000106601|31570010000000116301|31570010000000122800|31570010000000126001|3157002CKDQOCIHE5H01|31570020000000005900|31570029808HJVCNFA01|3157003FQSSGS9OZ9E01|3157004PTVTDOKB46401|3157004R6CH6C1P4KX00|3157005RUI28M8FANK00|3157005VJE7A3MBUS201|3157005WT1SENAE17R00|31570058O0Z320C4GZ00|3157006B6JVZ5DFMSN00|3157006DE3SPNIUY9K01|3157006FR3JBBOLOMX01|3157006I3B6RSTPQLI00|3157006KT1EZ15OIXW00|315700659AALVVLVIO01|31570067WSCDST0S3F01|3157008VKYORMUNC5O01|3157008ZY7CW6LVU5J00|3157009FTHFDDK7FHW01|3157009OVCV07O4HXM00|315700T8U7IU4W8J3A01|315700BBRQHDWX6SHZ00|3157008KD17KROO7UT01)$"
14
39
 
15
- leiLexicalPattern = re.compile(r"^[0-9A-Z]{18}[0-9]{2}$")
40
+ # Turn the _validLeiDespiteChecksumFailPattern in to an immutable set as that is much faster to check
41
+ _validLeiDespiteChecksumFailSet = frozenset(
42
+ _validLeiDespiteChecksumFailPatternString[2:-2].split("|")
43
+ )
16
44
 
17
- validInvalidLeiPattern = re.compile("^(029200720E3M3A4D6D01|029200758D5M0AI3F601|315700X8JQ3IU0NGK501|3157007SCCESQAUH5Z01|315700TCC9NTEP7J8Z01|13250000000000000301|315700ADOXDR5PCY5400|315700A0UB9Q7DOQIZ00|315700BM9Z39TNTGQW00|315700BZ5F7DRYG2UM00|315700B8401GTFFY6X01|315700DJ07P6OX10FK01|315700DKCD4QSKLAMO01|315700D23JL5C1DZNT00|315700EIYO2TLSEGQ700|315700ET7M7VQ4C84R00|315700EZSEA51937KX01|315700GXBGM8DBYKHF01|315700GZA843JXKTJ400|315700G5G24XYL1TXH00|315700HS7WJ1B0SUWM01|315700HXOEOK58E58P01|315700HZU4SMI8LZTU00|315700I3W2AFHP8MNQ01|315700JICZ3SY5SAXX00|315700JKZH6I0067ND01|315700JO5E28SRE00Q01|315700JXUHL9H2C3P700|315700LDDN3RM7Y2MP00|315700LWYOZNQ7V1T100|315700MBYPT6PGKO7M01|315700MW2F0KFR45QW01|315700M5843O6DU83901|315700NNMGS8F3P2CN00|315700N0VEIBHP0NPQ01|315700OASRCM664PAW01|315700OFW4YCOBNX4U01|315700OVW93X0T3HP200|315700O666JVNCQU9X00|315700PLI0I7W8IOV400|315700PN3J57ZUNF1V00|315700PXKOSX7WQV4N00|315700P4N9VSLK5QZV01|315700P40OV6BT045900|315700P6TZOLP92KN801|315700P89WR82VNB8Z00|315700QGM4XWZE1I5N01|315700Q1S8O1UORF9700|315700RK8M4FAHMYAP01|315700RTEHY362KXWJ00|315700SNUXK41WMW5J00|315700S22RGYRIEEOT00|315700S3TF79ALV82F01|315700TF5Z7T28HZJK01|315700TWGZ89LLSRS000|315700TXNX10N8XH4K00|315700T2EEQAPBO0C301|315700T6T49EDM16YO01|315700UJ6N4LGKLNPB00|315700UKZXWXEO126601|315700UYFD5GF9R13F01|315700VG7PTE9EJJRX01|315700VITYR7AL4M9S01|315700VMAJZ9JZTXNQ00|315700WH3YMKHCVYW201|315700WKCDF4QGRRO200|315700WR4IHOO1M5LP00|315700WYOZ6994UATN00|315700WZPEIS41QDKE00|315700XEFYMA5EZ0P500|315700XE21UYOA3GAC01|315700XI4Z8GF5BDUJ01|315700XSCP1S8WOD8E01|315700X40GNCOUWJYR00|315700YS6RQ5TF3VBP01|315700Y1W7W1JHAUBW00|315700Y5JNQMMUF5ID01|3157000VAJWZ3P8ZED00|3157001KM8GOU7PXZY01|3157001K2LAL04D87901|3157001MLDD3SDFQA901|3157001TPR6K4GBTLN00|31570010000000006400|31570010000000009601|31570010000000019301|31570010000000025800|31570010000000029001|31570010000000035500|31570010000000038701|31570010000000045200|31570010000000048401|31570010000000054900|31570010000000064600|31570010000000067801|31570010000000077501|31570010000000084000|31570010000000087201|31570010000000096901|31570010000000103400|31570010000000106601|31570010000000116301|31570010000000122800|31570010000000126001|3157002CKDQOCIHE5H01|31570020000000005900|31570029808HJVCNFA01|3157003FQSSGS9OZ9E01|3157004PTVTDOKB46401|3157004R6CH6C1P4KX00|3157005RUI28M8FANK00|3157005VJE7A3MBUS201|3157005WT1SENAE17R00|31570058O0Z320C4GZ00|3157006B6JVZ5DFMSN00|3157006DE3SPNIUY9K01|3157006FR3JBBOLOMX01|3157006I3B6RSTPQLI00|3157006KT1EZ15OIXW00|315700659AALVVLVIO01|31570067WSCDST0S3F01|3157008VKYORMUNC5O01|3157008ZY7CW6LVU5J00|3157009FTHFDDK7FHW01|3157009OVCV07O4HXM00|315700T8U7IU4W8J3A01|315700BBRQHDWX6SHZ00|3157008KD17KROO7UT01)$")
18
45
 
19
- def checkLei(lei: str) -> int:
20
- if validInvalidLeiPattern.match(lei):
21
- return LEI_VALID
22
- if not leiLexicalPattern.match(lei):
23
- return LEI_INVALID_LEXICAL
24
- if not int(
25
- "".join({"0":"0", "1":"1", "2":"2", "3":"3", "4":"4", "5":"5", "6":"6", "7":"7", "8":"8", "9":"9",
26
- "A":"10", "B":"11", "C":"12", "D":"13", "E":"14", "F":"15", "G":"16", "H":"17", "I":"18",
27
- "J":"19", "K":"20", "L":"21", "M":"22", "N":"23", "O":"24", "P":"25", "Q":"26", "R":"27",
28
- "S":"28", "T":"29", "U":"30", "V":"31", "W":"32", "X":"33", "Y":"34", "Z":"35"
29
- }[c] for c in lei)
30
- ) % 97 == 1:
31
- return LEI_INVALID_CHECKSUM
32
- return LEI_VALID
46
+ def checkLei(lei: str) -> LEIValidationResult:
47
+ if len(lei) != _requiredLEILength:
48
+ return LEIValidationResult.INVALID_LEXICAL
49
+
50
+ if not _leiLexicalPattern.fullmatch(lei):
51
+ return LEIValidationResult.INVALID_LEXICAL
52
+
53
+ result = LEIValidationResult.INVALID_CHECKSUM
54
+
55
+ if int(lei.translate(_leiToDigitTable)) % 97 == 1:
56
+ result = LEIValidationResult.VALID
57
+
58
+ if (
59
+ result is LEIValidationResult.INVALID_CHECKSUM
60
+ and lei in _validLeiDespiteChecksumFailSet
61
+ ):
62
+ result = LEIValidationResult.VALID
63
+
64
+ return result
65
+
33
66
 
34
67
  if __name__ == "__main__":
35
- # test cases
36
- for lei, name in (
37
- ("001GPB6A9XPE8XJICC14", "Fidelity Advisor Series I"),
38
- ("004L5FPTUREIWK9T2N63", "Hutchin Hill Capital, LP"),
39
- ("00EHHQ2ZHDCFXJCPCL46", "Vanguard Russell 1000 Growth Index Trust"),
40
- ("00GBW0Z2GYIER7DHDS71", "Aristeia Capital, L.L.C."),
41
- ("1S619D6B3ZQIH6MS6B47", "Barclays Vie SA"),
42
- ("21380014JAZAUFJRHC43", "BRE/OPERA HOLDINGS"),
43
- ("21380016W7GAG26FIJ74", "SOCIETE FRANCAISE ET SUISSE"),
44
- ("21380058ERUIT9H53T71", "TOTAN ICAP CO., LTD"),
45
- ("213800A9GT65GAES2V60", "BARCLAYS SECURITIES JAPAN LIMITED"),
46
- ("213800DELL1MWFDHVN53", "PIRELLI JAPAN"),
47
- ("213800A9GT65GAES2V60", "BARCLAYS SECURITIES JAPAN LIMITED"),
48
- ("214800A9GT65GAES2V60", "Error 1"),
49
- ("213800A9GT65GAE%2V60", "Error 2"),
50
- ("213800A9GT65GAES2V62", "Error 3"),
51
- ("1234", "Error 4"),
52
- ("""
53
- 5299003M8JKHEFX58Y02""", "Error 4")
54
- ):
55
- print ("LEI {} result {} name {}".format(lei, LEI_RESULTS[checkLei(lei)], name) )
68
+ import sys
69
+
70
+ leis = sys.argv[1:]
71
+ if not leis:
72
+ raise SystemExit("Specify the LEI(s) you want to check as arguments.")
73
+
74
+ for arg in sys.argv[1:]:
75
+ print(f"{arg:20s} : {checkLei(arg).description()}")
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.58'
32
- __version_tuple__ = version_tuple = (2, 37, 58)
31
+ __version__ = version = '2.37.60'
32
+ __version_tuple__ = version_tuple = (2, 37, 60)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -11,6 +11,8 @@ class AccountingStandard(Enum):
11
11
  US_GAAP = 'US GAAP'
12
12
 
13
13
 
14
+ domainItemTypeQname = qname("{http://www.xbrl.org/dtr/type/non-numeric}nonnum:domainItemType")
15
+
14
16
  qnEdinetManifestInsert = qname("{http://disclosure.edinet-fsa.go.jp/2013/manifest}insert")
15
17
  qnEdinetManifestInstance = qname("{http://disclosure.edinet-fsa.go.jp/2013/manifest}instance")
16
18
  qnEdinetManifestItem = qname("{http://disclosure.edinet-fsa.go.jp/2013/manifest}item")
@@ -42,8 +42,6 @@ from .UploadContents import UploadContents
42
42
  _: TypeGetText
43
43
 
44
44
 
45
- _DEBIT_QNAME_PATTERN = regex.compile('.*(Liability|Liabilities|Equity)')
46
-
47
45
  STANDARD_TAXONOMY_URL_PREFIXES = frozenset((
48
46
  'http://disclosure.edinet-fsa.go.jp/taxonomy/',
49
47
  'https://disclosure.edinet-fsa.go.jp/taxonomy/',
@@ -172,12 +170,6 @@ class PluginValidationDataExtension(PluginData):
172
170
  fileSourcePath = fullPath.relative_to(basePath)
173
171
  controllerPluginData.addUsedFilepath(fileSourcePath)
174
172
 
175
- def _isDebitConcept(self, concept: ModelConcept) -> bool:
176
- """
177
- :return: Whether the given concept is a debit concept.
178
- """
179
- return bool(_DEBIT_QNAME_PATTERN.match(concept.qname.localName))
180
-
181
173
  @lru_cache(1)
182
174
  def isCorporateForm(self, modelXbrl: ModelXbrl) -> bool:
183
175
  formTypes = self.getFormTypes(modelXbrl)
@@ -247,23 +239,23 @@ class PluginValidationDataExtension(PluginData):
247
239
  for (contextId, unitId), facts in factsByContextIdAndUnitId.items():
248
240
  if not self._contextMatchesStatement(modelXbrl, contextId, statement):
249
241
  continue
250
- assetSum = Decimal(0)
251
- liabilitiesAndEquitySum = Decimal(0)
242
+ creditSum = Decimal(0)
243
+ debitSum = Decimal(0)
252
244
  for fact in facts:
253
245
  if isinstance(fact.xValue, float):
254
246
  value = Decimal(fact.xValue)
255
247
  else:
256
248
  value = cast(Decimal, fact.xValue)
257
- if self._isDebitConcept(fact.concept):
258
- liabilitiesAndEquitySum += value
259
- else:
260
- assetSum += value
249
+ if fact.concept.balance == "debit":
250
+ debitSum += value
251
+ elif fact.concept.balance == "credit":
252
+ creditSum += value
261
253
  balanceSheets.append(
262
254
  BalanceSheet(
263
- assetsTotal=assetSum,
255
+ creditSum=creditSum,
264
256
  contextId=str(contextId),
265
257
  facts=facts,
266
- liabilitiesAndEquityTotal=liabilitiesAndEquitySum,
258
+ debitSum=debitSum,
267
259
  unitId=str(unitId),
268
260
  )
269
261
  )
@@ -481,6 +473,17 @@ class PluginValidationDataExtension(PluginData):
481
473
  def isStandardTaxonomyUrl(self, uri: str, modelXbrl: ModelXbrl) -> bool:
482
474
  return modelXbrl.modelManager.disclosureSystem.hrefValidForDisclosureSystem(uri)
483
475
 
476
+ def iterCoverPages(self, modelXbrl: ModelXbrl) -> Iterable[ModelDocument]:
477
+ uploadContents = self.getUploadContents(modelXbrl)
478
+ if uploadContents is None:
479
+ return
480
+ for url, doc in modelXbrl.urlDocs.items():
481
+ path = Path(url)
482
+ pathInfo = uploadContents.uploadPathsByFullPath.get(path)
483
+ if pathInfo is None or not pathInfo.isCoverPage:
484
+ continue
485
+ yield doc
486
+
484
487
  def iterFacts(self, modelXbrl: ModelXbrl, qname: QName) -> Iterable[ModelFact]:
485
488
  yield from modelXbrl.factsByQname.get(qname, set())
486
489
 
@@ -101,10 +101,10 @@ class Statement:
101
101
 
102
102
  @dataclass(frozen=True)
103
103
  class BalanceSheet:
104
- assetsTotal: Decimal
104
+ creditSum: Decimal
105
105
  contextId: str
106
106
  facts: list[ModelFact]
107
- liabilitiesAndEquityTotal: Decimal
107
+ debitSum: Decimal
108
108
  unitId: str
109
109
 
110
110
 
@@ -22,6 +22,8 @@ RELEVELER_MAP: dict[str, dict[str, tuple[str, str | None]]] = {
22
22
  "xbrl.4.8.2:sharesFactUnit-notSharesMeasure": ("ERROR", None),
23
23
  # Silence, duplicated by EDINET.EC5002E
24
24
  "xbrl.4.8.2:sharesFactUnit-notSingleMeasure": ("ERROR", None),
25
+ # Silence, duplicated by EDINET.EC5700W.GFM.1.7.2
26
+ "xbrl.5.2.5.2.1:zeroWeight": ("ERROR", None),
25
27
  },
26
28
  }
27
29
 
@@ -44,7 +44,7 @@ def rule_balances(
44
44
  for statementInstance in pluginData.getStatementInstances(val.modelXbrl):
45
45
  statement = statementInstance.statement
46
46
  for balanceSheet in statementInstance.balanceSheets:
47
- if balanceSheet.assetsTotal == balanceSheet.liabilitiesAndEquityTotal:
47
+ if balanceSheet.creditSum == balanceSheet.debitSum:
48
48
  continue
49
49
  code = None
50
50
  if statement.statementType == StatementType.BALANCE_SHEET:
@@ -62,15 +62,15 @@ def rule_balances(
62
62
  codes=code,
63
63
  msg=_("The %(consolidated)s %(balanceSheet)s is not balanced. "
64
64
  "The sum of all liabilities and equity must equal the sum of all assets. "
65
- "Please correct the debit (%(liabilitiesAndEquitySum)s) and credit (%(assetSum)s) "
65
+ "Please correct the debit (%(debitSum)s) and credit (%(creditSum)s) "
66
66
  "values so that they match "
67
67
  "<roleUri=%(roleUri)s> <contextID=%(contextId)s> <unitID=%(unitId)s>."),
68
68
  consolidated=_("consolidated") if statement.isConsolidated
69
69
  else _("nonconsolidated"),
70
70
  balanceSheet=_("balance sheet") if statement.statementType == StatementType.BALANCE_SHEET
71
71
  else _("statement of financial position"),
72
- liabilitiesAndEquitySum=f"{balanceSheet.liabilitiesAndEquityTotal:,}",
73
- assetSum=f"{balanceSheet.assetsTotal:,}",
72
+ debitSum=f"{balanceSheet.debitSum:,}",
73
+ creditSum=f"{balanceSheet.creditSum:,}",
74
74
  roleUri=statement.roleUri,
75
75
  contextId=balanceSheet.contextId,
76
76
  unitId=balanceSheet.unitId,
@@ -93,14 +93,27 @@ 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.getFormTypes(val.modelXbrl)
97
- if len(dei) > 0:
96
+ facts = [
97
+ fact
98
+ for qname in (
99
+ pluginData.jpcrpEsrFilingDateCoverPageQn,
100
+ pluginData.jpcrpFilingDateCoverPageQn,
101
+ pluginData.jpspsFilingDateCoverPageQn
102
+ )
103
+ for fact in pluginData.iterValidNonNilFacts(val.modelXbrl, qname)
104
+ ]
105
+ for modelDocument in pluginData.iterCoverPages(val.modelXbrl):
106
+ if any(fact.modelDocument == modelDocument for fact in facts):
107
+ continue
98
108
  if not (pluginData.hasValidNonNilFact(val.modelXbrl, pluginData.jpcrpEsrFilingDateCoverPageQn)
99
109
  or pluginData.hasValidNonNilFact(val.modelXbrl, pluginData.jpcrpFilingDateCoverPageQn)
100
110
  or pluginData.hasValidNonNilFact(val.modelXbrl, pluginData.jpspsFilingDateCoverPageQn)):
101
111
  yield Validation.error(
102
112
  codes='EDINET.EC1057E',
103
- msg=_("The [Submission Date] on the cover page has not been filled in."),
113
+ msg=_("There is no submission date ('【提出日】') on the cover page. "
114
+ "File name: '%(file)s'. "
115
+ "Please add '【提出日】' to the relevant file."),
116
+ file=modelDocument.basename,
104
117
  )
105
118
 
106
119
 
@@ -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.LinkbaseType import LinkbaseType
13
14
  from arelle.ModelDtsObject import ModelConcept
14
15
  from arelle.ModelInstanceObject import ModelFact
15
16
  from arelle.ModelObject import ModelObject
@@ -27,7 +28,7 @@ from arelle.utils.Units import getDuplicateUnitGroups
27
28
  from arelle.utils.validate.Decorator import validation
28
29
  from arelle.utils.validate.Validation import Validation
29
30
  from arelle.utils.validate.ValidationUtil import etreeIterWithDepth
30
- from ..Constants import NUMERIC_LABEL_ROLES
31
+ from ..Constants import NUMERIC_LABEL_ROLES, domainItemTypeQname
31
32
  from ..DisclosureSystems import (DISCLOSURE_SYSTEM_EDINET)
32
33
  from ..PluginValidationDataExtension import PluginValidationDataExtension
33
34
 
@@ -675,6 +676,35 @@ def rule_gfm_1_3_8(
675
676
  )
676
677
 
677
678
 
679
+ @validation(
680
+ hook=ValidationHook.XBRL_FINALLY,
681
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
682
+ )
683
+ def rule_gfm_1_3_19(
684
+ pluginData: PluginValidationDataExtension,
685
+ val: ValidateXbrl,
686
+ *args: Any,
687
+ **kwargs: Any,
688
+ ) -> Iterable[Validation]:
689
+ """
690
+ EDINET.EC5700W: [GFM 1.3.19] The id attribute of the element defined in the submitter-specific taxonomy
691
+ should be set in the following format:{namespace prefix}_{element name}.
692
+ """
693
+ improperlyFormattedIds = set()
694
+ for concept in pluginData.getExtensionConcepts(val.modelXbrl):
695
+ prefix = concept.qname.prefix or ""
696
+ name = concept.qname.localName
697
+ requiredId = f"{prefix}_{name}"
698
+ if concept.id != requiredId or not prefix:
699
+ improperlyFormattedIds.add(concept)
700
+ if len(improperlyFormattedIds) > 0:
701
+ yield Validation.warning(
702
+ codes='EDINET.EC5700W.GFM.1.3.19',
703
+ msg=_("The id attribute of the element defined in the submitter-specific taxonomy should be set in the following format: {namespace prefix}_{element name}"),
704
+ modelObject=improperlyFormattedIds
705
+ )
706
+
707
+
678
708
  @validation(
679
709
  hook=ValidationHook.XBRL_FINALLY,
680
710
  disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
@@ -857,10 +887,13 @@ def rule_gfm_1_5_6(
857
887
  labelRels = labelRelationshipSet.fromModelObject(concept)
858
888
  for rel in labelRels:
859
889
  label = rel.toModelObject
860
- if label.role != XbrlConst.documentationLabel and label.viewText() is not None and len(label.viewText()) >= 511:
890
+ if (label is not None and
891
+ label.role != XbrlConst.documentationLabel and
892
+ label.viewText() is not None and
893
+ len(label.viewText()) >= 511):
861
894
  yield Validation.warning(
862
895
  codes='EDINET.EC5700W.GFM.1.5.6',
863
- msg=_("The concept of '%(concept)s' has a label classified as '%(role)s' that is longer than 511 characters: %(label)s"),
896
+ msg=_("The concept of '%(concept)s' has a label classified as '%(role)s' that is greater than or equal to 511 characters: %(label)s"),
864
897
  concept=concept.qname,
865
898
  role=label.role,
866
899
  label=label.viewText(),
@@ -889,7 +922,7 @@ def rule_gfm_1_5_7(
889
922
  labelRels = labelRelationshipSet.fromModelObject(concept)
890
923
  for rel in labelRels:
891
924
  label = rel.toModelObject
892
- if label.role != XbrlConst.documentationLabel and label.textValue is not None:
925
+ if label is not None and label.role != XbrlConst.documentationLabel and label.textValue is not None:
893
926
  if '<' in label.textValue:
894
927
  yield Validation.warning(
895
928
  codes='EDINET.EC5700W.GFM.1.5.7',
@@ -930,7 +963,7 @@ def rule_gfm_1_5_8(
930
963
  labelRels = labelRelationshipSet.fromModelObject(concept)
931
964
  for rel in labelRels:
932
965
  label = rel.toModelObject
933
- if label.textValue is not None and label.textValue != label.textValue.strip():
966
+ if label is not None and label.textValue is not None and label.textValue != label.textValue.strip():
934
967
  yield Validation.warning(
935
968
  codes='EDINET.EC5700W.GFM.1.5.8',
936
969
  msg=_("The concept of '%(concept)s' has a label that contains disallowed white space either at the begining or the end: '%(label)s'"),
@@ -962,7 +995,9 @@ def rule_gfm_1_5_10(
962
995
  labelRels = labelRelationshipSet.fromModelObject(concept)
963
996
  for rel in labelRels:
964
997
  label = rel.toModelObject
965
- if not pluginData.isStandardTaxonomyUrl(label.modelDocument.uri, val.modelXbrl) and label.role in NUMERIC_LABEL_ROLES:
998
+ if (label is not None and
999
+ not pluginData.isStandardTaxonomyUrl(label.modelDocument.uri, val.modelXbrl) and
1000
+ label.role in NUMERIC_LABEL_ROLES):
966
1001
  yield Validation.warning(
967
1002
  codes='EDINET.EC5700W.GFM.1.5.10',
968
1003
  msg=_("The non-numeric concept of '%(concept)s' has a label with a numeric role of '%(labelrole)s'"),
@@ -970,3 +1005,331 @@ def rule_gfm_1_5_10(
970
1005
  labelrole=label.role,
971
1006
  modelObject=label
972
1007
  )
1008
+
1009
+
1010
+ @validation(
1011
+ hook=ValidationHook.XBRL_FINALLY,
1012
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
1013
+ )
1014
+ def rule_gfm_1_6_1(
1015
+ pluginData: PluginValidationDataExtension,
1016
+ val: ValidateXbrl,
1017
+ *args: Any,
1018
+ **kwargs: Any,
1019
+ ) -> Iterable[Validation]:
1020
+ """
1021
+ EDINET.EC5700W: [GFM 1.6.1] All presentation relationships must have an order attribute
1022
+ """
1023
+ presentationRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.PRESENTATION.getArcroles()))
1024
+ if presentationRelationshipSet is None:
1025
+ return
1026
+ for rel in presentationRelationshipSet.modelRelationships:
1027
+ if not rel.arcElement.get("order"):
1028
+ yield Validation.warning(
1029
+ codes='EDINET.EC5700W.GFM.1.6.1',
1030
+ msg=_("The presentation relationship is missing the order attribute"),
1031
+ modelObject=rel
1032
+ )
1033
+
1034
+
1035
+ @validation(
1036
+ hook=ValidationHook.XBRL_FINALLY,
1037
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
1038
+ )
1039
+ def rule_gfm_1_6_2(
1040
+ pluginData: PluginValidationDataExtension,
1041
+ val: ValidateXbrl,
1042
+ *args: Any,
1043
+ **kwargs: Any,
1044
+ ) -> Iterable[Validation]:
1045
+ """
1046
+ EDINET.EC5700W: [GFM 1.6.2] Presentation relationships must have unique order attributes
1047
+ """
1048
+ presentationRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.PRESENTATION.getArcroles()))
1049
+ if presentationRelationshipSet is None:
1050
+ return
1051
+ for modelObject, rels in presentationRelationshipSet.fromModelObjects().items():
1052
+ if len(rels) <= 1:
1053
+ continue
1054
+ relsByOrder = defaultdict(list)
1055
+ for rel in rels:
1056
+ order = rel.arcElement.get("order")
1057
+ if order is not None:
1058
+ relsByOrder[(order, rel.linkrole)].append(rel)
1059
+ for key, orderRels in relsByOrder.items():
1060
+ if len(orderRels) > 1:
1061
+ yield Validation.warning(
1062
+ codes='EDINET.EC5700W.GFM.1.6.2',
1063
+ msg=_("The presentation relationships have the same order attribute: '%(order)s'"),
1064
+ order=key[0],
1065
+ modelObject=orderRels
1066
+ )
1067
+
1068
+
1069
+ @validation(
1070
+ hook=ValidationHook.XBRL_FINALLY,
1071
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
1072
+ )
1073
+ def rule_gfm_1_6_5(
1074
+ pluginData: PluginValidationDataExtension,
1075
+ val: ValidateXbrl,
1076
+ *args: Any,
1077
+ **kwargs: Any,
1078
+ ) -> Iterable[Validation]:
1079
+ """
1080
+ EDINET.EC5700W: [GFM 1.6.5] If an element used in an instance is the target in the instance DTS of more than one
1081
+ effective presentation arc in a base set with the same source element, then the
1082
+ presentation arcs must have distinct values of the preferredLabel attribute.
1083
+ """
1084
+ presentationRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.PRESENTATION.getArcroles()))
1085
+ if presentationRelationshipSet is None:
1086
+ return
1087
+ for modelObject, rels in presentationRelationshipSet.toModelObjects().items():
1088
+ if len(rels) <= 1:
1089
+ continue
1090
+ relsByFrom = defaultdict(list)
1091
+ for rel in rels:
1092
+ relsByFrom[(rel.fromModelObject, rel.preferredLabel, rel.linkrole)].append(rel)
1093
+ for key, fromRels in relsByFrom.items():
1094
+ if len(fromRels) > 1:
1095
+ yield Validation.warning(
1096
+ codes='EDINET.EC5700W.GFM.1.6.5',
1097
+ msg=_("The presentation relationships must have distinct values of the preferredLabel attribute "
1098
+ "when they have the same source and target elements"),
1099
+ modelObject=fromRels
1100
+ )
1101
+
1102
+
1103
+ @validation(
1104
+ hook=ValidationHook.XBRL_FINALLY,
1105
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
1106
+ )
1107
+ def rule_gfm_1_7_1(
1108
+ pluginData: PluginValidationDataExtension,
1109
+ val: ValidateXbrl,
1110
+ *args: Any,
1111
+ **kwargs: Any,
1112
+ ) -> Iterable[Validation]:
1113
+ """
1114
+ EDINET.EC5700W: [GFM 1.7.1] All calculation relationships must have an order attribute
1115
+ """
1116
+ calculationRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.CALCULATION.getArcroles()))
1117
+ if calculationRelationshipSet is None:
1118
+ return
1119
+ for rel in calculationRelationshipSet.modelRelationships:
1120
+ if not rel.arcElement.get("order"):
1121
+ yield Validation.warning(
1122
+ codes='EDINET.EC5700W.GFM.1.7.1',
1123
+ msg=_("The calculation relationship is missing the order attribute"),
1124
+ modelObject=rel
1125
+ )
1126
+
1127
+
1128
+ @validation(
1129
+ hook=ValidationHook.XBRL_FINALLY,
1130
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
1131
+ )
1132
+ def rule_gfm_1_7_2(
1133
+ pluginData: PluginValidationDataExtension,
1134
+ val: ValidateXbrl,
1135
+ *args: Any,
1136
+ **kwargs: Any,
1137
+ ) -> Iterable[Validation]:
1138
+ """
1139
+ EDINET.EC5700W: [GFM 1.7.2] All calculation relationships must have a weight of either 1 or -1
1140
+ """
1141
+ calculationRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.CALCULATION.getArcroles()))
1142
+ if calculationRelationshipSet is None:
1143
+ return
1144
+ for rel in calculationRelationshipSet.modelRelationships:
1145
+ if rel.weight not in [1, -1]:
1146
+ yield Validation.warning(
1147
+ codes='EDINET.EC5700W.GFM.1.7.2',
1148
+ msg=_("The calculation relationship must have a weight of 1 or -1, actual weight: '%(weight)s'"),
1149
+ weight=rel.weight,
1150
+ modelObject=rel
1151
+ )
1152
+
1153
+
1154
+ @validation(
1155
+ hook=ValidationHook.XBRL_FINALLY,
1156
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
1157
+ )
1158
+ def rule_gfm_1_7_3(
1159
+ pluginData: PluginValidationDataExtension,
1160
+ val: ValidateXbrl,
1161
+ *args: Any,
1162
+ **kwargs: Any,
1163
+ ) -> Iterable[Validation]:
1164
+ """
1165
+ EDINET.EC5700W: [GFM 1.7.3] The concepts participating in a calculation relationship must have the same period type
1166
+ """
1167
+ calculationRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.CALCULATION.getArcroles()))
1168
+ if calculationRelationshipSet is None:
1169
+ return
1170
+ for rel in calculationRelationshipSet.modelRelationships:
1171
+ fromConcept = rel.fromModelObject
1172
+ toConcept = rel.toModelObject
1173
+ if fromConcept is not None and toConcept is not None and fromConcept.periodType != toConcept.periodType:
1174
+ yield Validation.warning(
1175
+ codes='EDINET.EC5700W.GFM.1.7.3',
1176
+ msg=_("The concepts participating in a calculation relationship must have the same period types. "
1177
+ "The concept of '%(concept1)s' has a period type of '%(concept1PeriodType)s' and the concept "
1178
+ "of '%(concept2)s' has a period type of '%(concept2PeriodType)s'"),
1179
+ concept1=fromConcept.qname,
1180
+ concept1PeriodType=fromConcept.periodType,
1181
+ concept2=toConcept.qname,
1182
+ concept2PeriodType=toConcept.periodType,
1183
+ modelObject=rel
1184
+ )
1185
+
1186
+
1187
+ @validation(
1188
+ hook=ValidationHook.XBRL_FINALLY,
1189
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
1190
+ )
1191
+ def rule_gfm_1_7_6(
1192
+ pluginData: PluginValidationDataExtension,
1193
+ val: ValidateXbrl,
1194
+ *args: Any,
1195
+ **kwargs: Any,
1196
+ ) -> Iterable[Validation]:
1197
+ """
1198
+ EDINET.EC5700W: [GFM 1.7.6] Calculation relationships must have unique order attributes
1199
+ """
1200
+ calculationRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.CALCULATION.getArcroles()))
1201
+ if calculationRelationshipSet is None:
1202
+ return
1203
+ for modelObject, rels in calculationRelationshipSet.fromModelObjects().items():
1204
+ if len(rels) <= 1:
1205
+ continue
1206
+ relsByOrder = defaultdict(list)
1207
+ for rel in rels:
1208
+ order = rel.arcElement.get("order")
1209
+ if order is not None:
1210
+ relsByOrder[(order, rel.linkrole)].append(rel)
1211
+ for key, orderRels in relsByOrder.items():
1212
+ if len(orderRels) > 1:
1213
+ yield Validation.warning(
1214
+ codes='EDINET.EC5700W.GFM.1.7.6',
1215
+ msg=_("The calculation relationships have the same order attribute: '%(order)s'"),
1216
+ order=key[0],
1217
+ modelObject=orderRels
1218
+ )
1219
+
1220
+
1221
+ @validation(
1222
+ hook=ValidationHook.XBRL_FINALLY,
1223
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
1224
+ )
1225
+ def rule_gfm_1_8_1(
1226
+ pluginData: PluginValidationDataExtension,
1227
+ val: ValidateXbrl,
1228
+ *args: Any,
1229
+ **kwargs: Any,
1230
+ ) -> Iterable[Validation]:
1231
+ """
1232
+ EDINET.EC5700W: [GFM 1.8.1] All definition relationships must have an order attribute
1233
+ """
1234
+ definitionRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.DEFINITION.getArcroles()))
1235
+ if definitionRelationshipSet is None:
1236
+ return
1237
+ for rel in definitionRelationshipSet.modelRelationships:
1238
+ if not rel.arcElement.get("order"):
1239
+ yield Validation.warning(
1240
+ codes='EDINET.EC5700W.GFM.1.8.1',
1241
+ msg=_("The definition relationship is missing the order attribute"),
1242
+ modelObject=rel
1243
+ )
1244
+
1245
+
1246
+ @validation(
1247
+ hook=ValidationHook.XBRL_FINALLY,
1248
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
1249
+ )
1250
+ def rule_gfm_1_8_3(
1251
+ pluginData: PluginValidationDataExtension,
1252
+ val: ValidateXbrl,
1253
+ *args: Any,
1254
+ **kwargs: Any,
1255
+ ) -> Iterable[Validation]:
1256
+ """
1257
+ EDINET.EC5700W: [GFM 1.8.3] The target of an effective arc with an xlink:arcrole attribute equal to
1258
+ "http://xbrl.org/int/dim/arcrole/dimension-domain" or
1259
+ "http://xbrl.org/int/arcrole/dimension-default" must be of type
1260
+ nonnum:domainItemType.
1261
+ """
1262
+ dimensionRelationshipSet = val.modelXbrl.relationshipSet((XbrlConst.dimensionDomain, XbrlConst.dimensionDefault))
1263
+ if dimensionRelationshipSet is None:
1264
+ return
1265
+ for rel in dimensionRelationshipSet.modelRelationships:
1266
+ toConcept = rel.toModelObject
1267
+ if toConcept is not None and toConcept.typeQname != domainItemTypeQname:
1268
+ yield Validation.warning(
1269
+ codes='EDINET.EC5700W.GFM.1.8.3',
1270
+ msg=_("The definition relationship target concept of '%(concept)s' has a type of '%(type)s' instead of 'nonnum:domainItemType'."),
1271
+ concept=toConcept.qname,
1272
+ type=toConcept.typeQname,
1273
+ modelObject=rel
1274
+ )
1275
+
1276
+
1277
+ @validation(
1278
+ hook=ValidationHook.XBRL_FINALLY,
1279
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
1280
+ )
1281
+ def rule_gfm_1_8_10(
1282
+ pluginData: PluginValidationDataExtension,
1283
+ val: ValidateXbrl,
1284
+ *args: Any,
1285
+ **kwargs: Any,
1286
+ ) -> Iterable[Validation]:
1287
+ """
1288
+ EDINET.EC5700W: [GFM 1.8.10] Definition relationships must have unique order attributes
1289
+ """
1290
+ definitionRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.DEFINITION.getArcroles()))
1291
+ if definitionRelationshipSet is None:
1292
+ return
1293
+ for modelObject, rels in definitionRelationshipSet.loadModelRelationshipsFrom().items():
1294
+ if len(rels) <= 1:
1295
+ continue
1296
+ relsByOrder = defaultdict(list)
1297
+ for rel in rels:
1298
+ order = rel.arcElement.get("order")
1299
+ if order is not None:
1300
+ relsByOrder[(order, rel.linkrole, rel.arcrole)].append(rel)
1301
+ for key, orderRels in relsByOrder.items():
1302
+ if len(orderRels) > 1:
1303
+ yield Validation.warning(
1304
+ codes='EDINET.EC5700W.GFM.1.8.10',
1305
+ msg=_("The definition relationships have the same order attribute: '%(order)s'"),
1306
+ order=key[0],
1307
+ modelObject=orderRels
1308
+ )
1309
+
1310
+
1311
+ @validation(
1312
+ hook=ValidationHook.XBRL_FINALLY,
1313
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
1314
+ )
1315
+ def rule_gfm_1_8_11(
1316
+ pluginData: PluginValidationDataExtension,
1317
+ val: ValidateXbrl,
1318
+ *args: Any,
1319
+ **kwargs: Any,
1320
+ ) -> Iterable[Validation]:
1321
+ """
1322
+ EDINET.EC5700W: [GFM 1.8.11] Definition relationships can not have the xbrldt:usable attribute set to False
1323
+ """
1324
+ definitionRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.DEFINITION.getArcroles()))
1325
+ if definitionRelationshipSet is None:
1326
+ return
1327
+ for rel in definitionRelationshipSet.modelRelationships:
1328
+ if rel.arcrole in [XbrlConst.dimensionDomain, XbrlConst.domainMember]:
1329
+ continue
1330
+ if not rel.isUsable:
1331
+ yield Validation.warning(
1332
+ codes='EDINET.EC5700W.GFM.1.8.11',
1333
+ msg=_("The definition relationship can not have the xbrldt:usable attribute set to False"),
1334
+ modelObject=rel
1335
+ )
@@ -271,19 +271,37 @@ def rule_EC0132E(
271
271
  **kwargs: Any,
272
272
  ) -> Iterable[Validation]:
273
273
  """
274
- EDINET.EC0132E: Store the manifest file directly under the relevant folder.
274
+ EDINET.EC0132E: Cover page or manifest file is missing.
275
+
276
+ Note: Cover page is not required in AuditDoc.
275
277
  """
276
278
  uploadContents = pluginData.getUploadContents()
277
279
  if uploadContents is None:
278
280
  return
279
281
  for reportFolderType, paths in uploadContents.reports.items():
280
282
  if reportFolderType.isAttachment:
283
+ # These rules don't apply to "Attach" directories
281
284
  continue
282
- if reportFolderType.manifestPath not in paths:
285
+ coverPageFound = False
286
+ manifestFound = False
287
+ for path in paths:
288
+ pathInfo = uploadContents.uploadPathsByPath[path]
289
+ if pathInfo.isCoverPage:
290
+ coverPageFound = True
291
+ if path == reportFolderType.manifestPath:
292
+ manifestFound = True
293
+ if not coverPageFound and reportFolderType != ReportFolderType.AUDIT_DOC:
294
+ yield Validation.error(
295
+ codes='EDINET.EC0132E',
296
+ msg=_("Cover page does not exist in '%(expectedManifestDirectory)s'. "
297
+ "Please store the cover file directly under the relevant folder and upload it again. "),
298
+ expectedManifestDirectory=str(reportFolderType.manifestPath.parent),
299
+ )
300
+ if not manifestFound:
283
301
  yield Validation.error(
284
302
  codes='EDINET.EC0132E',
285
303
  msg=_("'%(expectedManifestName)s' does not exist in '%(expectedManifestDirectory)s'. "
286
- "Please store the manifest file (or cover file) directly under the relevant folder and upload it again. "),
304
+ "Please store the manifest file directly under the relevant folder and upload it again. "),
287
305
  expectedManifestName=reportFolderType.manifestPath.name,
288
306
  expectedManifestDirectory=str(reportFolderType.manifestPath.parent),
289
307
  )
@@ -25,6 +25,7 @@ CO_AUDIT = 'Co.Audit'
25
25
  CO_AUDIT_NR = 'Co.AuditNR'
26
26
  CO_DIR_REP = 'Co.DirReport'
27
27
  CO_DIR_RESP = 'Co.DirResp'
28
+ CO_GROUP = 'Co.Group'
28
29
  CO_MED_CO = 'Co.MedCo'
29
30
  CO_MICRO = 'Co.Micro'
30
31
  CO_MISSING_ELEMENT = 'Co.MissingElement'
@@ -35,6 +36,7 @@ CO_SEC_477 = 'Co.Sec477'
35
36
  CO_SEC_480 = 'Co.Sec480'
36
37
  LP_ABRID = 'Lp.Abrid'
37
38
  LP_AUDIT = 'Lp.Audit'
39
+ LP_GROUP = 'Lp.Group'
38
40
  LP_MED_LP = 'Lp.MedLp'
39
41
  LP_MEM_RESP = 'Lp.MemResp'
40
42
  LP_MICRO = 'Lp.Micro'
@@ -57,19 +59,23 @@ CONCEPT_ACCOUNTS_STATUS_DIMENSION = 'AccountsStatusDimension'
57
59
  CONCEPT_ACCOUNTS_TYPE_FULL_OR_ABBREVIATED = 'AccountsTypeFullOrAbbreviated' # DEPRECATED IN 2022+ taxonomies. No replacement yet.
58
60
  CONCEPT_ACCOUNTS_TYPE_DIMENSION = 'AccountsTypeDimension'
59
61
  CONCEPT_ADVERSE_OPINION = 'AdverseOpinion'
62
+ CONCEPT_BALANCE_SHEET_DATE = 'BalanceSheetDate'
60
63
  CONCEPT_CHARITY_FUNDS = 'CharityFunds'
61
64
  CONCEPT_CHARITY_REGISTRATION_NUMBER_ENGLAND_WALES = 'CharityRegistrationNumberEnglandWales'
62
65
  CONCEPT_CHARITY_REGISTRATION_NUMBER_NORTH_IRELAND = 'CharityRegistrationNumberNorthernIreland'
63
66
  CONCEPT_CHARITY_REGISTRATION_NUMBER_SCOTLAND = 'CharityRegistrationNumberScotland'
67
+ CONCEPT_CONSOLIDATED = 'Consolidated'
64
68
  CONCEPT_DATE_AUDITOR_REPORT = 'DateAuditorsReport'
65
69
  CONCEPT_DATE_CHARITY_AUDITORS_REPORT = 'DateCharityAuditorsReport'
66
70
  CONCEPT_DATE_SIGNING_DIRECTOR_REPORT = 'DateSigningDirectorsReport'
67
71
  CONCEPT_DATE_SIGNING_TRUSTEES_REPORT = 'DateSigningTrusteesAnnualReport'
68
72
  CONCEPT_DIRECTOR_SIGNING_DIRECTORS_REPORT = 'DirectorSigningDirectorsReport'
69
73
  CONCEPT_DISCLAIMER_OPINION = 'DisclaimerOpinion'
74
+ CONCEPT_END_DATE_FOR_PERIOD_COVERED_BY_REPORT = 'EndDateForPeriodCoveredByReport'
70
75
  CONCEPT_ENTITY_DORMANT = 'EntityDormantTruefalse'
71
76
  CONCEPT_ENTITY_TRADING_STATUS = 'EntityTradingStatus'
72
77
  CONCEPT_ENTITY_TRADING_STATUS_DIMENSION = 'EntityTradingStatusDimension'
78
+ CONCEPT_GROUP_COMPANY_DATA_DIMENSION = 'GroupCompanyDataDimension'
73
79
  CONCEPT_LANGUAGES_DIMENSION = 'LanguagesDimension'
74
80
  CONCEPT_MEDIUM_COMPANY = 'StatementThatCompanyHasPreparedAccountsUnderProvisionsRelatingToMedium-sizedCompanies'
75
81
  CONCEPT_MEDIUM_COMPANIES_REGIME_FOR_ACCOUNTS = 'Medium-sizedCompaniesRegimeForAccounts'
@@ -646,6 +652,39 @@ class ValidateUK:
646
652
  )
647
653
  return CodeResult()
648
654
 
655
+ def _evaluateGroupFacts(self) -> CodeResult:
656
+ """
657
+ BalanceSheetDate with GroupCompanyDataDimension and Consolidated must equal BalanceSheetDate with default
658
+ dimensions. Both facts must be non-nil.
659
+ """
660
+ consolidatedFact = None
661
+ defaultFact = None
662
+ endDateFact = None
663
+ for fact in self._getFacts(CONCEPT_END_DATE_FOR_PERIOD_COVERED_BY_REPORT):
664
+ if not fact.context.qnameDims:
665
+ endDateFact = fact
666
+ break
667
+ if endDateFact is None:
668
+ return CodeResult()
669
+ for balanceSheetDateFact in self._getFacts(CONCEPT_BALANCE_SHEET_DATE):
670
+ if self._checkValidFact(balanceSheetDateFact):
671
+ if not balanceSheetDateFact.context.qnameDims and balanceSheetDateFact.context.instantDate == endDateFact.context.instantDate:
672
+ defaultFact = balanceSheetDateFact
673
+ for qname, value in balanceSheetDateFact.context.qnameDims.items():
674
+ if value.xValid < VALID:
675
+ continue
676
+ if qname.localName == CONCEPT_GROUP_COMPANY_DATA_DIMENSION and cast(str, value.xValue.localName) == CONCEPT_CONSOLIDATED:
677
+ consolidatedFact = balanceSheetDateFact
678
+ break
679
+ if consolidatedFact is None or defaultFact is None or consolidatedFact.xValue != defaultFact.xValue:
680
+ return CodeResult(
681
+ conceptLocalName=CONCEPT_BALANCE_SHEET_DATE,
682
+ success=False,
683
+ message="A fact tagged with BalanceSheetDate with the dimension of GroupCompanyDataDimension/Consolidated "
684
+ "must equal a fact tagged with BalanceSheetDate with the default dimension."
685
+ )
686
+ return CodeResult()
687
+
649
688
  def _evaluateCode(self, code: str) -> CodeResult:
650
689
  """
651
690
  Evaluates whether the conditions associated with the given code pass.
@@ -667,6 +706,8 @@ class ValidateUK:
667
706
  result = self._evaluateProfLossOrCharityFundsFact(code)
668
707
  elif code == CH_AUDIT:
669
708
  result = self._evaluateCharAuditFacts()
709
+ elif code in (CO_GROUP, LP_GROUP):
710
+ result = self._evaluateGroupFacts()
670
711
  return self._setCode(code, result)
671
712
 
672
713
  def _getFacts(self, conceptLocalName: str) -> list[ModelFact]:
@@ -831,6 +872,14 @@ class ValidateUK:
831
872
  self.validateAuditedOtherLLP()
832
873
  else:
833
874
  self.validateAuditedOtherCompany()
875
+ elif self.scopeAccounts in {
876
+ ScopeAccounts.GROUP_ONLY.value,
877
+ ScopeAccounts.CONSOLIDATED_GROUP.value,
878
+ }:
879
+ if self.legalFormEntity == CONCEPT_LLP:
880
+ self.validateAuditedGroupLLP()
881
+ else:
882
+ self.validateAuditedGroupCompany()
834
883
 
835
884
  def validateCharities(self) -> None:
836
885
  """
@@ -1271,3 +1320,53 @@ class ValidateUK:
1271
1320
  result = self._evaluateCode(CO_SM_CO)
1272
1321
  if not result.success:
1273
1322
  self._errorOnMissingFactText(CO_SM_CO, result)
1323
+
1324
+ def validateAuditedGroupCompany(self) -> None:
1325
+ """
1326
+ Checks conditions applicable to audited group companies:
1327
+ Co.Audit AND ((Co.DirReport AND Co.ProfLoss AND Co.Group) OR Co.SmCo).
1328
+ """
1329
+ result = self._evaluateCode(CO_AUDIT)
1330
+ if not result.success:
1331
+ self._yieldErrorOrWarning(CO_AUDIT, result)
1332
+
1333
+ result = self._evaluateCode(CO_SM_CO)
1334
+ if not result.success:
1335
+ failedOr = False
1336
+ dirRepResult = self._evaluateCode(CO_DIR_REP)
1337
+ if not dirRepResult.success:
1338
+ self._yieldErrorOrWarning(CO_DIR_REP, dirRepResult)
1339
+ failedOr = True
1340
+ profLossResult = self._evaluateCode(CO_PROF_LOSS)
1341
+ if not profLossResult.success:
1342
+ self._yieldErrorOrWarning(CO_PROF_LOSS, profLossResult)
1343
+ failedOr = True
1344
+ groupResult = self._evaluateCode(CO_GROUP)
1345
+ if not groupResult.success:
1346
+ self._yieldErrorOrWarning(CO_GROUP, groupResult)
1347
+ failedOr = True
1348
+ if failedOr:
1349
+ self._errorOnMissingFactText(CO_SM_CO, result)
1350
+
1351
+ def validateAuditedGroupLLP(self) -> None:
1352
+ """
1353
+ Checks conditions applicable to audited group companies:
1354
+ LP.Audit AND ((LP.ProfLoss+LP.Group) OR LP.SmLp)
1355
+ """
1356
+ result = self._evaluateCode(LP_AUDIT)
1357
+ if not result.success:
1358
+ self._yieldErrorOrWarning(LP_AUDIT, result)
1359
+
1360
+ result = self._evaluateCode(LP_SM_LP)
1361
+ if not result.success:
1362
+ failedOr = False
1363
+ profLossResult = self._evaluateCode(LP_PROF_LOSS)
1364
+ if not profLossResult.success:
1365
+ self._yieldErrorOrWarning(LP_PROF_LOSS, profLossResult)
1366
+ failedOr = True
1367
+ groupResult = self._evaluateCode(LP_GROUP)
1368
+ if not groupResult.success:
1369
+ self._yieldErrorOrWarning(LP_GROUP, groupResult)
1370
+ failedOr = True
1371
+ if failedOr:
1372
+ self._errorOnMissingFactText(LP_SM_LP, result)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arelle-release
3
- Version: 2.37.58
3
+ Version: 2.37.60
4
4
  Summary: An open source XBRL platform.
5
5
  Author-email: "arelle.org" <support@arelle.org>
6
6
  License-Expression: Apache-2.0
@@ -59,7 +59,7 @@ Requires-Dist: tinycss2<2,>=1; extra == "esef"
59
59
  Provides-Extra: objectmaker
60
60
  Requires-Dist: graphviz<1,>=0; extra == "objectmaker"
61
61
  Provides-Extra: webserver
62
- Requires-Dist: cheroot<11,>=8; extra == "webserver"
62
+ Requires-Dist: cheroot<12,>=8; extra == "webserver"
63
63
  Requires-Dist: tornado<7,>=6; extra == "webserver"
64
64
  Provides-Extra: xule
65
65
  Requires-Dist: aniso8601<11,>=10; extra == "xule"
@@ -32,7 +32,7 @@ arelle/FunctionXs.py,sha256=q0SA0e5wLRxH6kbNUOnChktcGVxaB7A6CE_O3nceysA,13163
32
32
  arelle/HashUtil.py,sha256=dS11q9UUUFzP7MgiuNdxFfvF3IpeyZUiOFxVFArEEWE,2963
33
33
  arelle/HtmlUtil.py,sha256=eXa6yF5xBCOODCuBMY7g8ePZHPOSu6X0gDsW3z9N50A,761
34
34
  arelle/InstanceAspectsEvaluator.py,sha256=TePNIs_m0vCIbN5N4PXEyJm529T2WBFi2zmv-72r3Zk,1805
35
- arelle/LeiUtil.py,sha256=tSPrbQrXEeH5pXgGA_6MAdgMZp20NaW5izJglIXyEQk,5095
35
+ arelle/LeiUtil.py,sha256=yWbyU1Qnd81iYHLcyDi0I13GZkjaxCL04YVNQ6jqaP8,4698
36
36
  arelle/LinkbaseType.py,sha256=BwRQl4XZFFCopufC2FEMLhYENNTk2JUWVQvnIUsaqtI,3108
37
37
  arelle/LocalViewer.py,sha256=WVrfek_bLeFFxgWITi1EQb6xCQN8O9Ks-ZL16vRncSk,3080
38
38
  arelle/Locale.py,sha256=07IDxv8nIfvhyRRllFdEAKRI3yo1D2v781cTrjb_RoQ,33193
@@ -125,7 +125,7 @@ arelle/XmlValidateConst.py,sha256=U_wN0Q-nWKwf6dKJtcu_83FXPn9c6P8JjzGA5b0w7P0,33
125
125
  arelle/XmlValidateParticles.py,sha256=Mn6vhFl0ZKC_vag1mBwn1rH_x2jmlusJYqOOuxFPO2k,9231
126
126
  arelle/XmlValidateSchema.py,sha256=6frtZOc1Yrx_5yYF6V6oHbScnglWrVbWr6xW4EHtLQI,7428
127
127
  arelle/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
128
- arelle/_version.py,sha256=DMtGvhO601kTygJwWMKYB_n05a0AM-s0DoCNqsyf8lE,708
128
+ arelle/_version.py,sha256=koFp4Z0c56Yr9CtycRp6LnXPGQS-w-XkLeKmAMM0VDM,708
129
129
  arelle/typing.py,sha256=PRe-Fxwr2SBqYYUVPCJ3E7ddDX0_oOISNdT5Q97EbRM,1246
130
130
  arelle/api/Session.py,sha256=kgSxS7VckA1sQ7xp0pJiK7IK-vRxAdAZKUo8gEx27s8,7549
131
131
  arelle/config/creationSoftwareNames.json,sha256=5MK7XUjfDJ9OpRCCHXeOErJ1SlTBZji4WEcEOdOacx0,3128
@@ -312,29 +312,29 @@ 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=6LVzBTt4045SxUjeQa37tlN8zYLF1QIz16khYo3eY4A,2418
315
+ arelle/plugin/validate/EDINET/Constants.py,sha256=I8d-oc9wG-mImV-3Cv3tInP3QjfWO7DeltbuOepyF58,2514
316
316
  arelle/plugin/validate/EDINET/ControllerPluginData.py,sha256=1WhiS0RdrxeXz4pGDzWATEPqCopOh2spr8Z6Qra_Psk,8420
317
317
  arelle/plugin/validate/EDINET/CoverPageRequirements.py,sha256=ZR8pk1CQfUIi15as1zVF27W0kRlUF1M_Ygw7appDUno,4488
318
318
  arelle/plugin/validate/EDINET/DisclosureSystems.py,sha256=3rKG42Eg-17Xx_KXU_V5yHW6I3LTwQunvf4a44C9k_4,36
319
319
  arelle/plugin/validate/EDINET/FilingFormat.py,sha256=SFZ22zFk6RVIA9dpx3iVLlf2heKfZZqt2ZUXUje4BII,18789
320
320
  arelle/plugin/validate/EDINET/FormType.py,sha256=jFqjJACJJ4HhkY1t6Fqei0z6rgvH3Mp-dP04KwQVv3Q,2517
321
321
  arelle/plugin/validate/EDINET/ManifestInstance.py,sha256=o6BGlaQHSsn6D0VKH4zn59UscKnjTKlo99kSGfGdYlU,6910
322
- arelle/plugin/validate/EDINET/PluginValidationDataExtension.py,sha256=9YXy4f1EIYq4NGWaOP7dXjVStdtH4Mb4D4wACrIa2PU,22540
322
+ arelle/plugin/validate/EDINET/PluginValidationDataExtension.py,sha256=iA72MJv4r5hhLY5w9YBqQ279RI6ndivXGW-PqqK5rJM,22663
323
323
  arelle/plugin/validate/EDINET/ReportFolderType.py,sha256=Q-9a-5tJfhK-cjY8WUB2AT1NI-Nn9cFtARVOIJoLRGU,2979
324
- arelle/plugin/validate/EDINET/Statement.py,sha256=0Mw5IB7LMtvUZ-2xKZfxmq67xF_dCgJo3eNLweLFRHU,9350
324
+ arelle/plugin/validate/EDINET/Statement.py,sha256=CGq8c647pIEBQtOv8AL0U4knT8HofOdK8IaUjHrcOYU,9331
325
325
  arelle/plugin/validate/EDINET/UploadContents.py,sha256=o29mDoX48M3S2jQqrJO4ZaulltAPt4vD-qdsWTMCUPc,1196
326
326
  arelle/plugin/validate/EDINET/ValidationPluginExtension.py,sha256=8LNqvXzNaWP54dShEjet5ely4BnM8ByCSyimKpUx3_s,2577
327
- arelle/plugin/validate/EDINET/__init__.py,sha256=ECWgqzwHA7MZ3g7SeoFI7ttR9Wq_lywV-TlqeUW_juY,3186
327
+ arelle/plugin/validate/EDINET/__init__.py,sha256=T_p2phcMw1lR4J1X4gvqJPEcZNJdJXtaPpc159A-3_8,3298
328
328
  arelle/plugin/validate/EDINET/resources/config.xml,sha256=7uT4GcRgk5veMLpFhPPQJxbGKiQvM52P8EMrjn0qd0g,646
329
329
  arelle/plugin/validate/EDINET/resources/cover-page-requirements.csv,sha256=8ILKNn8bXbcgG9V0lc8mxorKDKEjJQWLdBQRMvbqtkI,6518
330
330
  arelle/plugin/validate/EDINET/resources/edinet-taxonomies.xml,sha256=997I3RGTLg5OY3vn5hQxVFAAxOmDSOYpuyQe6VnWSY0,16285
331
331
  arelle/plugin/validate/EDINET/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
332
332
  arelle/plugin/validate/EDINET/rules/contexts.py,sha256=KPoyWfRaURvxoGVcWP64mTMTAKPMSmQSX06RClCLddw,7590
333
- arelle/plugin/validate/EDINET/rules/edinet.py,sha256=g3IPMV5-mWbVwVmEwv5-h1LOtdfeBQ9gZI27m744FFw,12628
333
+ arelle/plugin/validate/EDINET/rules/edinet.py,sha256=1dHYl0nqZ5ql0SaE9Jj95fGegOMlssQNayeebV0iCJQ,13118
334
334
  arelle/plugin/validate/EDINET/rules/frta.py,sha256=N0YglHYZuLD2IuwE26viR2ViwUYjneBuMFU9vlrS0aQ,7616
335
- arelle/plugin/validate/EDINET/rules/gfm.py,sha256=-NHyo6QAkKoekAc5nLSpvSwQ0lImttoSD-_y1puK-y4,36741
335
+ arelle/plugin/validate/EDINET/rules/gfm.py,sha256=Khiipm6s98RxScd_caU_xpbzFDAnfNy925iDxtCV5zs,50915
336
336
  arelle/plugin/validate/EDINET/rules/manifests.py,sha256=MoT9R_a4BzuYdQVbF7RC5wz134Ve68svSdJ3NlpO_AU,4026
337
- arelle/plugin/validate/EDINET/rules/upload.py,sha256=E_sEhsuUkoH648PGWwpN90uOQMjKNFaZ8priucmYMOk,47908
337
+ arelle/plugin/validate/EDINET/rules/upload.py,sha256=YdjVIRTO9Zrmdprx-y0uo6MzHP7YLYYItwbR8AjP_fE,48731
338
338
  arelle/plugin/validate/ESEF/Const.py,sha256=JujF_XV-_TNsxjGbF-8SQS4OOZIcJ8zhCMnr-C1O5Ho,22660
339
339
  arelle/plugin/validate/ESEF/Dimensions.py,sha256=MOJM7vwNPEmV5cu-ZzPrhx3347ZvxgD6643OB2HRnIk,10597
340
340
  arelle/plugin/validate/ESEF/Util.py,sha256=QH3btcGqBpr42M7WSKZLSdNXygZaZLfEiEjlxoG21jE,7950
@@ -370,7 +370,7 @@ arelle/plugin/validate/ROS/config.xml,sha256=ZCpCFgr1ZAjoUuhb1eRpDnmKrae-sXA9yl6
370
370
  arelle/plugin/validate/ROS/resources/config.xml,sha256=HXWume5HlrAqOx5AtiWWqgADbRatA8YSfm_JvZGwdgQ,657
371
371
  arelle/plugin/validate/ROS/rules/__init__.py,sha256=wW7BUAIb7sRkOxC1Amc_ZKrz03FM-Qh1TyZe6wxYaAU,1567
372
372
  arelle/plugin/validate/ROS/rules/ros.py,sha256=CRZkZfsKe4y1B-PDkazQ_cD5LRZBk1GJjTscCZXXYUI,21173
373
- arelle/plugin/validate/UK/ValidateUK.py,sha256=h7-tnCubHme8Meaif-o55TV2rCfMWuikfpZCcK6NNDs,56447
373
+ arelle/plugin/validate/UK/ValidateUK.py,sha256=UwNR3mhFzWNn6RKLSiC7j3LlMlConOx-9gWGovKamHA,61035
374
374
  arelle/plugin/validate/UK/__init__.py,sha256=GT67T_7qpOASzdmgRXFPmLxfPg3Gjubli7luQDK3zck,27462
375
375
  arelle/plugin/validate/UK/config.xml,sha256=mUFhWDfBzGTn7v0ZSmf4HaweQTMJh_4ZcJmD9mzCHrA,1547
376
376
  arelle/plugin/validate/UK/consistencyChecksByName.json,sha256=BgB9YAWzmcsX-_rU74RBkABwEsS75vrMlwBHsYCz2R0,25247
@@ -684,9 +684,9 @@ arelle/utils/validate/ValidationUtil.py,sha256=9vmSvShn-EdQy56dfesyV8JjSRVPj7txr
684
684
  arelle/utils/validate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
685
685
  arelle/webserver/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
686
686
  arelle/webserver/bottle.py,sha256=P-JECd9MCTNcxCnKoDUvGcoi03ezYVOgoWgv2_uH-6M,362
687
- arelle_release-2.37.58.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
688
- arelle_release-2.37.58.dist-info/METADATA,sha256=R7D4feFh8MfRYsXoyMrffu1IDXMxju2PLquC1Ht1H-0,9327
689
- arelle_release-2.37.58.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
690
- arelle_release-2.37.58.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
691
- arelle_release-2.37.58.dist-info/top_level.txt,sha256=fwU7SYawL4_r-sUMRg7r1nYVGjFMSDvRWx8VGAXEw7w,7
692
- arelle_release-2.37.58.dist-info/RECORD,,
687
+ arelle_release-2.37.60.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
688
+ arelle_release-2.37.60.dist-info/METADATA,sha256=raUd5lPyMS3Tio2TTVEQ5W-W4Ha-IBqpWvJN3o2jOCc,9327
689
+ arelle_release-2.37.60.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
690
+ arelle_release-2.37.60.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
691
+ arelle_release-2.37.60.dist-info/top_level.txt,sha256=fwU7SYawL4_r-sUMRg7r1nYVGjFMSDvRWx8VGAXEw7w,7
692
+ arelle_release-2.37.60.dist-info/RECORD,,