arelle-release 2.37.58__py3-none-any.whl → 2.37.59__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.59'
32
+ __version_tuple__ = version_tuple = (2, 37, 59)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -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
 
@@ -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
@@ -970,3 +971,239 @@ def rule_gfm_1_5_10(
970
971
  labelrole=label.role,
971
972
  modelObject=label
972
973
  )
974
+
975
+
976
+ @validation(
977
+ hook=ValidationHook.XBRL_FINALLY,
978
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
979
+ )
980
+ def rule_gfm_1_6_1(
981
+ pluginData: PluginValidationDataExtension,
982
+ val: ValidateXbrl,
983
+ *args: Any,
984
+ **kwargs: Any,
985
+ ) -> Iterable[Validation]:
986
+ """
987
+ EDINET.EC5700W: [GFM 1.6.1] All presentation relationships must have an order attribute
988
+ """
989
+ presentationRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.PRESENTATION.getArcroles()))
990
+ if presentationRelationshipSet is None:
991
+ return
992
+ for rel in presentationRelationshipSet.modelRelationships:
993
+ if not rel.arcElement.get("order"):
994
+ yield Validation.warning(
995
+ codes='EDINET.EC5700W.GFM.1.6.1',
996
+ msg=_("The presentation relationship is missing the order attribute"),
997
+ modelObject=rel
998
+ )
999
+
1000
+
1001
+ @validation(
1002
+ hook=ValidationHook.XBRL_FINALLY,
1003
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
1004
+ )
1005
+ def rule_gfm_1_6_2(
1006
+ pluginData: PluginValidationDataExtension,
1007
+ val: ValidateXbrl,
1008
+ *args: Any,
1009
+ **kwargs: Any,
1010
+ ) -> Iterable[Validation]:
1011
+ """
1012
+ EDINET.EC5700W: [GFM 1.6.2] Presentation relationships must have unique order attributes
1013
+ """
1014
+ presentationRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.PRESENTATION.getArcroles()))
1015
+ if presentationRelationshipSet is None:
1016
+ return
1017
+ for modelObject, rels in presentationRelationshipSet.fromModelObjects().items():
1018
+ if len(rels) <= 1:
1019
+ continue
1020
+ relsByOrder = defaultdict(list)
1021
+ for rel in rels:
1022
+ order = rel.arcElement.get("order")
1023
+ if order is not None:
1024
+ relsByOrder[(order, rel.linkrole)].append(rel)
1025
+ for key, orderRels in relsByOrder.items():
1026
+ if len(orderRels) > 1:
1027
+ yield Validation.warning(
1028
+ codes='EDINET.EC5700W.GFM.1.6.2',
1029
+ msg=_("The presentation relationships have the same order attribute: '%(order)s'"),
1030
+ order=key[0],
1031
+ modelObject=orderRels
1032
+ )
1033
+
1034
+
1035
+ @validation(
1036
+ hook=ValidationHook.XBRL_FINALLY,
1037
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
1038
+ )
1039
+ def rule_gfm_1_6_5(
1040
+ pluginData: PluginValidationDataExtension,
1041
+ val: ValidateXbrl,
1042
+ *args: Any,
1043
+ **kwargs: Any,
1044
+ ) -> Iterable[Validation]:
1045
+ """
1046
+ EDINET.EC5700W: [GFM 1.6.5] If an element used in an instance is the target in the instance DTS of more than one
1047
+ effective presentation arc in a base set with the same source element, then the
1048
+ presentation arcs must have distinct values of the preferredLabel attribute.
1049
+ """
1050
+ presentationRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.PRESENTATION.getArcroles()))
1051
+ if presentationRelationshipSet is None:
1052
+ return
1053
+ for modelObject, rels in presentationRelationshipSet.toModelObjects().items():
1054
+ if len(rels) <= 1:
1055
+ continue
1056
+ relsByFrom = defaultdict(list)
1057
+ for rel in rels:
1058
+ relsByFrom[(rel.fromModelObject, rel.preferredLabel, rel.linkrole)].append(rel)
1059
+ for key, fromRels in relsByFrom.items():
1060
+ if len(fromRels) > 1:
1061
+ yield Validation.warning(
1062
+ codes='EDINET.EC5700W.GFM.1.6.5',
1063
+ msg=_("The presentation relationships must have distinct values of the preferredLabel attribute "
1064
+ "when they have the same source and target elements"),
1065
+ modelObject=fromRels
1066
+ )
1067
+
1068
+
1069
+ @validation(
1070
+ hook=ValidationHook.XBRL_FINALLY,
1071
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
1072
+ )
1073
+ def rule_gfm_1_7_1(
1074
+ pluginData: PluginValidationDataExtension,
1075
+ val: ValidateXbrl,
1076
+ *args: Any,
1077
+ **kwargs: Any,
1078
+ ) -> Iterable[Validation]:
1079
+ """
1080
+ EDINET.EC5700W: [GFM 1.7.1] All calculation relationships must have an order attribute
1081
+ """
1082
+ calculationRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.CALCULATION.getArcroles()))
1083
+ if calculationRelationshipSet is None:
1084
+ return
1085
+ for rel in calculationRelationshipSet.modelRelationships:
1086
+ if not rel.arcElement.get("order"):
1087
+ yield Validation.warning(
1088
+ codes='EDINET.EC5700W.GFM.1.7.1',
1089
+ msg=_("The calculation relationship is missing the order attribute"),
1090
+ modelObject=rel
1091
+ )
1092
+
1093
+
1094
+ @validation(
1095
+ hook=ValidationHook.XBRL_FINALLY,
1096
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
1097
+ )
1098
+ def rule_gfm_1_7_2(
1099
+ pluginData: PluginValidationDataExtension,
1100
+ val: ValidateXbrl,
1101
+ *args: Any,
1102
+ **kwargs: Any,
1103
+ ) -> Iterable[Validation]:
1104
+ """
1105
+ EDINET.EC5700W: [GFM 1.7.2] All calculation relationships must have a weight of either 1 or -1
1106
+ """
1107
+ calculationRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.CALCULATION.getArcroles()))
1108
+ if calculationRelationshipSet is None:
1109
+ return
1110
+ for rel in calculationRelationshipSet.modelRelationships:
1111
+ if rel.weight not in [1, -1]:
1112
+ yield Validation.warning(
1113
+ codes='EDINET.EC5700W.GFM.1.7.2',
1114
+ msg=_("The calculation relationship must have a weight of 1 or -1, actual weight: '%(weight)s'"),
1115
+ weight=rel.weight,
1116
+ modelObject=rel
1117
+ )
1118
+
1119
+
1120
+ @validation(
1121
+ hook=ValidationHook.XBRL_FINALLY,
1122
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
1123
+ )
1124
+ def rule_gfm_1_7_3(
1125
+ pluginData: PluginValidationDataExtension,
1126
+ val: ValidateXbrl,
1127
+ *args: Any,
1128
+ **kwargs: Any,
1129
+ ) -> Iterable[Validation]:
1130
+ """
1131
+ EDINET.EC5700W: [GFM 1.7.3] The concepts participating in a calculation relationship must have the same period type
1132
+ """
1133
+ calculationRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.CALCULATION.getArcroles()))
1134
+ if calculationRelationshipSet is None:
1135
+ return
1136
+ for rel in calculationRelationshipSet.modelRelationships:
1137
+ fromConcept = rel.fromModelObject
1138
+ toConcept = rel.toModelObject
1139
+ if fromConcept is not None and toConcept is not None and fromConcept.periodType != toConcept.periodType:
1140
+ yield Validation.warning(
1141
+ codes='EDINET.EC5700W.GFM.1.7.3',
1142
+ msg=_("The concepts participating in a calculation relationship must have the same period types. "
1143
+ "The concept of '%(concept1)s' has a period type of '%(concept1PeriodType)s' and the concept "
1144
+ "of '%(concept2)s' has a period type of '%(concept2PeriodType)s'"),
1145
+ concept1=fromConcept.qname,
1146
+ concept1PeriodType=fromConcept.periodType,
1147
+ concept2=toConcept.qname,
1148
+ concept2PeriodType=toConcept.periodType,
1149
+ modelObject=rel
1150
+ )
1151
+
1152
+
1153
+ @validation(
1154
+ hook=ValidationHook.XBRL_FINALLY,
1155
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
1156
+ )
1157
+ def rule_gfm_1_7_6(
1158
+ pluginData: PluginValidationDataExtension,
1159
+ val: ValidateXbrl,
1160
+ *args: Any,
1161
+ **kwargs: Any,
1162
+ ) -> Iterable[Validation]:
1163
+ """
1164
+ EDINET.EC5700W: [GFM 1.7.6] Calculation relationships must have unique order attributes
1165
+ """
1166
+ calculationRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.CALCULATION.getArcroles()))
1167
+ if calculationRelationshipSet is None:
1168
+ return
1169
+ for modelObject, rels in calculationRelationshipSet.fromModelObjects().items():
1170
+ if len(rels) <= 1:
1171
+ continue
1172
+ relsByOrder = defaultdict(list)
1173
+ for rel in rels:
1174
+ order = rel.arcElement.get("order")
1175
+ if order is not None:
1176
+ relsByOrder[(order, rel.linkrole)].append(rel)
1177
+ for key, orderRels in relsByOrder.items():
1178
+ if len(orderRels) > 1:
1179
+ yield Validation.warning(
1180
+ codes='EDINET.EC5700W.GFM.1.7.6',
1181
+ msg=_("The calculation relationships have the same order attribute: '%(order)s'"),
1182
+ order=key[0],
1183
+ modelObject=orderRels
1184
+ )
1185
+
1186
+
1187
+ @validation(
1188
+ hook=ValidationHook.XBRL_FINALLY,
1189
+ disclosureSystems=[DISCLOSURE_SYSTEM_EDINET],
1190
+ )
1191
+ def rule_gfm_1_8_1(
1192
+ pluginData: PluginValidationDataExtension,
1193
+ val: ValidateXbrl,
1194
+ *args: Any,
1195
+ **kwargs: Any,
1196
+ ) -> Iterable[Validation]:
1197
+ """
1198
+ EDINET.EC5700W: [GFM 1.8.1] All definition relationships must have an order attribute
1199
+ """
1200
+ definitionRelationshipSet = val.modelXbrl.relationshipSet(tuple(LinkbaseType.DEFINITION.getArcroles()))
1201
+ if definitionRelationshipSet is None:
1202
+ return
1203
+ for rel in definitionRelationshipSet.modelRelationships:
1204
+ if not rel.arcElement.get("order"):
1205
+ yield Validation.warning(
1206
+ codes='EDINET.EC5700W.GFM.1.8.1',
1207
+ msg=_("The definition relationship is missing the order attribute"),
1208
+ modelObject=rel
1209
+ )
@@ -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.59
4
4
  Summary: An open source XBRL platform.
5
5
  Author-email: "arelle.org" <support@arelle.org>
6
6
  License-Expression: Apache-2.0
@@ -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=OtIOO8dIBzaWUcN4ZpZMg03PbgyJW5ESDOu8koDT508,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
@@ -324,7 +324,7 @@ arelle/plugin/validate/EDINET/ReportFolderType.py,sha256=Q-9a-5tJfhK-cjY8WUB2AT1
324
324
  arelle/plugin/validate/EDINET/Statement.py,sha256=0Mw5IB7LMtvUZ-2xKZfxmq67xF_dCgJo3eNLweLFRHU,9350
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
@@ -332,7 +332,7 @@ arelle/plugin/validate/EDINET/rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQ
332
332
  arelle/plugin/validate/EDINET/rules/contexts.py,sha256=KPoyWfRaURvxoGVcWP64mTMTAKPMSmQSX06RClCLddw,7590
333
333
  arelle/plugin/validate/EDINET/rules/edinet.py,sha256=g3IPMV5-mWbVwVmEwv5-h1LOtdfeBQ9gZI27m744FFw,12628
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=BAcH_yVnJw3abMfsUBk_skZAjB96h-lff795OdnozvQ,45905
336
336
  arelle/plugin/validate/EDINET/rules/manifests.py,sha256=MoT9R_a4BzuYdQVbF7RC5wz134Ve68svSdJ3NlpO_AU,4026
337
337
  arelle/plugin/validate/EDINET/rules/upload.py,sha256=E_sEhsuUkoH648PGWwpN90uOQMjKNFaZ8priucmYMOk,47908
338
338
  arelle/plugin/validate/ESEF/Const.py,sha256=JujF_XV-_TNsxjGbF-8SQS4OOZIcJ8zhCMnr-C1O5Ho,22660
@@ -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.59.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
688
+ arelle_release-2.37.59.dist-info/METADATA,sha256=wh7XkuU_1WbVuQhpnKA9ybkP_WlbD4eKla0WJ_2md1E,9327
689
+ arelle_release-2.37.59.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
690
+ arelle_release-2.37.59.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
691
+ arelle_release-2.37.59.dist-info/top_level.txt,sha256=fwU7SYawL4_r-sUMRg7r1nYVGjFMSDvRWx8VGAXEw7w,7
692
+ arelle_release-2.37.59.dist-info/RECORD,,