arelle-release 2.37.6__py3-none-any.whl → 2.37.8__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.
arelle/CntlrCmdLine.py CHANGED
@@ -603,7 +603,7 @@ def filesourceEntrypointFiles(filesource, entrypointFiles=None, inlineOnly=False
603
603
  pluginXbrlMethod(filesource, reportEntries)
604
604
  ixdsDiscovered = True
605
605
  if not ixdsDiscovered and len(reportEntries) > 1:
606
- raise RuntimeError(_("Loading error. Inline document set encountered. Enable 'InlineDocumentSet' plug-in to load this filing: {0}").format(filesource.url))
606
+ raise RuntimeError(_("Loading error. Inline document set encountered. Enable 'InlineXbrlDocumentSet' plug-in to load this filing: {0}").format(filesource.url))
607
607
  entrypointFiles.extend(reportEntries)
608
608
  elif not inlineOnly:
609
609
  entrypointFiles.append({"file": report.fullPathPrimary})
arelle/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '2.37.6'
21
- __version_tuple__ = version_tuple = (2, 37, 6)
20
+ __version__ = version = '2.37.8'
21
+ __version_tuple__ = version_tuple = (2, 37, 8)
arelle/oim/Load.py CHANGED
@@ -74,6 +74,7 @@ reservedLinkTypeAndGroupAliases = {
74
74
  XLINKTYPE = "{http://www.w3.org/1999/xlink}type"
75
75
  XLINKLABEL = "{http://www.w3.org/1999/xlink}label"
76
76
  XLINKARCROLE = "{http://www.w3.org/1999/xlink}arcrole"
77
+ XLINKROLE = "{http://www.w3.org/1999/xlink}role"
77
78
  XLINKFROM = "{http://www.w3.org/1999/xlink}from"
78
79
  XLINKTO = "{http://www.w3.org/1999/xlink}to"
79
80
  XLINKHREF = "{http://www.w3.org/1999/xlink}href"
@@ -2717,6 +2718,7 @@ def _loadFromOIM(cntlr, error, warning, modelXbrl, oimFile, mappedUri):
2717
2718
  footnoteLinkNotes[linkrole].add(noteId)
2718
2719
  xbrlNote = xbrlNoteTbl[noteId]
2719
2720
  attrs = {XLINKTYPE: "resource",
2721
+ XLINKROLE: XbrlConst.footnote,
2720
2722
  XLINKLABEL: footnoteToLabel,
2721
2723
  "id": idDeduped(modelXbrl, noteId),
2722
2724
  # "oimNoteId": noteId
@@ -2816,6 +2818,8 @@ def _loadFromOIM(cntlr, error, warning, modelXbrl, oimFile, mappedUri):
2816
2818
  modelObject=modelXbrl, action=currentAction, error=ex,
2817
2819
  traceback=traceback.format_tb(sys.exc_info()[2]))
2818
2820
 
2821
+ # Reset modified status of model so user is not prompted for changes triggered by this loading operation.
2822
+ _return.isModified = False
2819
2823
  return _return
2820
2824
 
2821
2825
  def _isParamRef(value):
@@ -59,6 +59,7 @@ from __future__ import annotations
59
59
  import csv
60
60
  import io
61
61
  import json
62
+ import operator
62
63
  import os
63
64
  import threading
64
65
  import zipfile
@@ -124,6 +125,17 @@ qnOimPeriodAspect = qname("period", noPrefixIsNoNamespace=True)
124
125
  qnOimEntityAspect = qname("entity", noPrefixIsNoNamespace=True)
125
126
  qnOimUnitAspect = qname("unit", noPrefixIsNoNamespace=True)
126
127
 
128
+ reservedUriAliases = {
129
+ nsOim: "xbrl",
130
+ XbrlConst.defaultLinkRole: "_",
131
+ XbrlConst.factExplanatoryFact: "explanatoryFact",
132
+ XbrlConst.factFootnote: "footnote",
133
+ XbrlConst.iso4217: "iso4217",
134
+ XbrlConst.utr: "utr",
135
+ XbrlConst.xbrli: "xbrli",
136
+ XbrlConst.xsd: "xs",
137
+ }
138
+
127
139
  ONE = Decimal(1)
128
140
  TEN = Decimal(10)
129
141
  NILVALUE = "nil"
@@ -142,6 +154,43 @@ csvOpenNewline = ""
142
154
  OimFact = dict[str, Any]
143
155
  OimReport = dict[str, Any]
144
156
 
157
+ class NamespacePrefixes:
158
+ def __init__(self, prefixesByNamespace: dict[str, str] | None = None) -> None:
159
+ self._prefixesByNamespace: dict[str, str] = prefixesByNamespace or {}
160
+ self._usedPrefixes: set[str] = set(self._prefixesByNamespace.values())
161
+
162
+ @property
163
+ def namespaces(self) -> dict[str, str]:
164
+ return {
165
+ prefix: namespace
166
+ for namespace, prefix in sorted(
167
+ self._prefixesByNamespace.items(),
168
+ key=operator.itemgetter(1)
169
+ )
170
+ }
171
+
172
+ def __contains__(self, namespace: str) -> bool:
173
+ return namespace in self._prefixesByNamespace
174
+
175
+ def getPrefix(self, namespace: str) -> str | None:
176
+ return self._prefixesByNamespace.get(namespace)
177
+
178
+ def addNamespace(self, namespace: str, preferredPrefix: str) -> str:
179
+ prefix = self._prefixesByNamespace.get(namespace)
180
+ if prefix is not None:
181
+ return prefix
182
+
183
+ prefix = reservedUriAliases.get(namespace)
184
+ if prefix is None:
185
+ prefix = preferredPrefix
186
+ i = 2
187
+ while prefix in self._usedPrefixes:
188
+ prefix = f"{preferredPrefix}{i}"
189
+ i += 1
190
+ self._prefixesByNamespace[namespace] = prefix
191
+ self._usedPrefixes.add(prefix)
192
+ return prefix
193
+
145
194
 
146
195
  def saveLoadableOIM(
147
196
  modelXbrl: ModelXbrl,
@@ -160,20 +209,19 @@ def saveLoadableOIM(
160
209
  isXL = oimFile.endswith(".xlsx")
161
210
  isCSVorXL = isCSV or isXL
162
211
  if not isJSON and not isCSVorXL:
163
- return
212
+ oimFile = oimFile + ".json"
213
+ isJSON = True
164
214
 
165
- namespacePrefixes = {nsOim: "xbrl"}
166
- prefixNamespaces: dict[str, str] = {}
215
+ namespacePrefixes = NamespacePrefixes({nsOim: "xbrl"})
167
216
  if extensionPrefixes:
168
217
  for extensionPrefix, extensionNamespace in extensionPrefixes.items():
169
- namespacePrefixes[extensionNamespace] = extensionPrefix
170
- prefixNamespaces[extensionPrefix] = extensionNamespace
218
+ namespacePrefixes.addNamespace(extensionNamespace, extensionPrefix)
171
219
  linkTypeAliases = {}
172
220
  groupAliases = {}
173
221
 
174
222
  def compileQname(qname: QName) -> None:
175
223
  if qname.namespaceURI is not None and qname.namespaceURI not in namespacePrefixes:
176
- namespacePrefixes[qname.namespaceURI] = qname.prefix or ""
224
+ namespacePrefixes.addNamespace(qname.namespaceURI, qname.prefix or "")
177
225
 
178
226
  aspectsDefined = {qnOimConceptAspect, qnOimEntityAspect, qnOimPeriodAspect}
179
227
 
@@ -183,12 +231,8 @@ def saveLoadableOIM(
183
231
  return " ".join([oimValue(o) for o in obj])
184
232
  if isinstance(obj, QName) and obj.namespaceURI is not None:
185
233
  if obj.namespaceURI not in namespacePrefixes:
186
- if obj.prefix:
187
- namespacePrefixes[obj.namespaceURI] = obj.prefix
188
- else:
189
- _prefix = "_{}".format(sum(1 for p in namespacePrefixes if p.startswith("_")))
190
- namespacePrefixes[obj.namespaceURI] = _prefix
191
- return f"{namespacePrefixes[obj.namespaceURI]}:{obj.localName}"
234
+ namespacePrefixes.addNamespace(obj.namespaceURI, obj.prefix or "_")
235
+ return f"{namespacePrefixes.getPrefix(obj.namespaceURI)}:{obj.localName}"
192
236
  if isinstance(obj, (float, Decimal)):
193
237
  try:
194
238
  if isinf(obj):
@@ -198,7 +242,11 @@ def saveLoadableOIM(
198
242
  elif isinstance(obj, Decimal):
199
243
  # XML canonical representation of decimal requires a decimal point.
200
244
  # https://www.w3.org/TR/xmlschema-2/#decimal-canonical-representation
201
- return f"{obj:.1f}" if obj % 1 == 0 else f"{obj}"
245
+ if obj % 1 == 0:
246
+ return f"{obj:.1f}"
247
+ intPart, fracPart = f"{obj:f}".split(".")
248
+ canonicalFracPart = fracPart.rstrip("0") or "0"
249
+ return f"{intPart}.{canonicalFracPart}"
202
250
  else:
203
251
  return f"{obj}"
204
252
  except Exception:
@@ -283,8 +331,8 @@ def saveLoadableOIM(
283
331
  _schemePrefix = "scheme"
284
332
  else:
285
333
  _schemePrefix = f"scheme{len(entitySchemePrefixes) + 1}"
286
- entitySchemePrefixes[scheme] = _schemePrefix
287
- namespacePrefixes[scheme] = _schemePrefix
334
+ namespacePrefixes.addNamespace(scheme, _schemePrefix)
335
+ entitySchemePrefixes[scheme] = namespacePrefixes.getPrefix(scheme)
288
336
  for dim in cntx.qnameDims.values():
289
337
  compileQname(dim.dimensionQname)
290
338
  aspectsDefined.add(dim.dimensionQname)
@@ -297,23 +345,17 @@ def saveLoadableOIM(
297
345
  for measure in measures:
298
346
  compileQname(measure)
299
347
 
300
- if XbrlConst.xbrli in namespacePrefixes and namespacePrefixes[XbrlConst.xbrli] != "xbrli":
301
- namespacePrefixes[XbrlConst.xbrli] = "xbrli" # normalize xbrli prefix
302
-
303
348
  if hasLang:
304
349
  aspectsDefined.add(qnOimLangAspect)
305
350
  if hasUnits:
306
351
  aspectsDefined.add(qnOimUnitAspect)
307
352
 
308
353
  for footnoteRel in footnotesRelationshipSet.modelRelationships:
309
- typePrefix = "ftTyp_" + os.path.basename(footnoteRel.arcrole)
310
- if footnoteRel.linkrole == XbrlConst.defaultLinkRole:
311
- groupPrefix = "ftGrp_default"
312
- else:
313
- groupPrefix = "ftGrp_" + os.path.basename(footnoteRel.linkrole)
314
354
  if footnoteRel.arcrole not in linkTypeAliases:
355
+ typePrefix = reservedUriAliases.get(footnoteRel.arcrole, f"ftTyp_{os.path.basename(footnoteRel.arcrole)}")
315
356
  linkTypeAliases[footnoteRel.arcrole] = typePrefix
316
- if groupPrefix not in groupAliases:
357
+ if footnoteRel.linkrole not in groupAliases:
358
+ groupPrefix = reservedUriAliases.get(footnoteRel.linkrole, f"ftGrp_{os.path.basename(footnoteRel.linkrole)}")
317
359
  groupAliases[footnoteRel.linkrole] = groupPrefix
318
360
 
319
361
  dtsReferences = set()
@@ -354,12 +396,14 @@ def saveLoadableOIM(
354
396
  _link[groupPrefix] = []
355
397
  elif isCSVorXL:
356
398
  _link[groupPrefix] = {}
399
+ tgtId = toObj.id if toObj.id else f"f{toObj.objectIndex}"
357
400
  if isJSON:
358
401
  tgtIdList = _link[groupPrefix]
402
+ tgtIdList.append(tgtId)
359
403
  elif isCSVorXL:
360
- tgtIdList = _link[groupPrefix].setdefault(srcId, [])
361
- tgtId = toObj.id if toObj.id else f"f{toObj.objectIndex}"
362
- tgtIdList.append(tgtId)
404
+ # Footnote links in xBRL-CSV include the CSV table identifier.
405
+ tgtIdList = _link[groupPrefix].setdefault(f"facts.r_{srcId}.value", [])
406
+ tgtIdList.append(f"footnotes.r_{tgtId}.footnote")
363
407
  footnote = {
364
408
  "group": footnoteRel.linkrole,
365
409
  "footnoteType": footnoteRel.arcrole,
@@ -441,19 +485,17 @@ def saveLoadableOIM(
441
485
  factFootnotes(fact, oimFact=oimFact)
442
486
  return oimFact
443
487
 
444
- namespaces = {p: ns for ns, p in sorted(namespacePrefixes.items(), key=lambda item: item[1])}
445
-
446
488
  # common metadata
447
489
  oimReport = {} # top level of oim json output
448
490
  oimReport["documentInfo"] = oimDocInfo = {}
449
491
  oimDocInfo["documentType"] = nsOim + ("/xbrl-json" if isJSON else "/xbrl-csv")
450
492
  if isJSON:
451
493
  oimDocInfo["features"] = oimFeatures = {}
452
- oimDocInfo["namespaces"] = namespaces
494
+ oimDocInfo["namespaces"] = namespacePrefixes.namespaces
453
495
  if linkTypeAliases:
454
- oimDocInfo["linkTypes"] = {a: u for u, a in sorted(linkTypeAliases.items(), key=lambda item: item[1])}
455
- if linkTypeAliases:
456
- oimDocInfo["linkGroups"] = {a: u for u, a in sorted(groupAliases.items(), key=lambda item: item[1])}
496
+ oimDocInfo["linkTypes"] = {a: u for u, a in sorted(linkTypeAliases.items(), key=operator.itemgetter(1))}
497
+ if groupAliases:
498
+ oimDocInfo["linkGroups"] = {a: u for u, a in sorted(groupAliases.items(), key=operator.itemgetter(1))}
457
499
  oimDocInfo["taxonomy"] = dtsReferences
458
500
  if isJSON:
459
501
  oimFeatures["xbrl:canonicalValues"] = True
@@ -511,6 +553,8 @@ def saveLoadableOIM(
511
553
  if outputZip:
512
554
  fh.seek(0)
513
555
  outputZip.writestr(os.path.basename(oimFile), fh.read())
556
+ if not outputZip:
557
+ modelXbrl.modelManager.cntlr.showStatus(_("Saved JSON OIM file {}").format(oimFile))
514
558
 
515
559
  elif isCSVorXL:
516
560
  # save CSV
@@ -678,21 +722,31 @@ def saveLoadableOIM(
678
722
 
679
723
  # save footnotes
680
724
  if footnotesRelationshipSet.modelRelationships:
681
- footnotes = sorted(
682
- (footnote for fact in modelXbrl.facts for footnote in factFootnotes(fact, csvLinks=csvLinks)),
683
- key=lambda footnote: footnote["id"],
684
- )
725
+ footnotesDeduplicatedById = {
726
+ footnote["id"]: footnote
727
+ for fact in modelXbrl.facts
728
+ for footnote in factFootnotes(fact, csvLinks=csvLinks)
729
+ }
730
+ footnotes = sorted(footnotesDeduplicatedById.values(), key=operator.itemgetter("id"))
685
731
  if footnotes: # text footnotes
686
- oimTables["footnotes"] = csvFtTable = {}
687
- csvFtTable["url"] = "tbd"
688
- csvFtTable["tableDimensions"] = {}
689
- csvFtTable["factColumns"] = csvFtFactColumns = {}
690
- csvFtFactColumns["footnote"] = csvFtValCol = {}
691
- csvFtValCol["id"] = "$id"
692
- csvFtValCol["noteId"] = "$id"
693
- csvFtValCol["concept"] = "xbrl:note"
694
- csvFtValCol["language"] = "$language"
695
- _open("-footnotes.csv", "footnotes", csvFtTable)
732
+ footnotesTable = {"template": "footnotes"}
733
+ oimTables["footnotes"] = footnotesTable
734
+ csvTableTemplates[footnotesTable["template"]] = {
735
+ "rowIdColumn" : "id",
736
+ "dimensions": {
737
+ "language": "$language"
738
+ },
739
+ "columns": {
740
+ "id": {},
741
+ "footnote": {
742
+ "dimensions": {
743
+ "concept": "xbrl:note",
744
+ },
745
+ },
746
+ "language": {},
747
+ }
748
+ }
749
+ _open("-footnotes.csv", "footnotes", footnotesTable)
696
750
  cols = ("id", "footnote", "language")
697
751
  _writerow(cols, header=True)
698
752
  for footnote in footnotes:
@@ -702,16 +756,17 @@ def saveLoadableOIM(
702
756
  # save metadata
703
757
  if isCSV:
704
758
  assert isinstance(_baseURL, str)
705
- with open(_baseURL + "-metadata.json", "w", encoding="utf-8") as fh:
759
+ csvMetadataFile = _baseURL + "-metadata.json"
760
+ with open(csvMetadataFile, "w", encoding="utf-8") as fh:
706
761
  fh.write(json.dumps(oimReport, ensure_ascii=False, indent=2, sort_keys=False))
762
+ modelXbrl.modelManager.cntlr.showStatus(_("Saved CSV OIM metadata file {}").format(csvMetadataFile))
707
763
  elif isXL:
708
764
  _open(None, "metadata")
709
765
  _writerow(["metadata"], header=True)
710
766
  _writerow([json.dumps(oimReport, ensure_ascii=False, indent=1, sort_keys=False)])
711
767
  _close()
712
-
713
- if isXL:
714
768
  workbook.save(oimFile)
769
+ modelXbrl.modelManager.cntlr.showStatus(_("Saved Excel file {}").format(oimFile))
715
770
 
716
771
 
717
772
  def saveLoadableOIMMenuCommand(cntlr: CntlrWinMain) -> None:
@@ -722,18 +777,23 @@ def saveLoadableOIMMenuCommand(cntlr: CntlrWinMain) -> None:
722
777
  or cntlr.modelManager.modelXbrl is None
723
778
  or cntlr.modelManager.modelXbrl.modelDocument is None
724
779
  or cntlr.modelManager.modelXbrl.modelDocument.type
725
- not in (ModelDocument.Type.INSTANCE, ModelDocument.Type.INLINEXBRL)
780
+ not in (ModelDocument.Type.INSTANCE, ModelDocument.Type.INLINEXBRL, ModelDocument.Type.INLINEXBRLDOCUMENTSET)
726
781
  ):
782
+ cntlr.addToLog(
783
+ messageCode="arelleOIMsaver",
784
+ message=_("No supported XBRL instance documents loaded that can be saved to OIM format."),
785
+ )
727
786
  return
728
787
  # get file name into which to save log file while in foreground thread
729
788
  oimFile = cntlr.uiFileDialog(
730
789
  "save",
731
790
  title=_("arelle - Save Loadable OIM file"),
732
- initialdir=cntlr.config.setdefault("loadableExcelFileDir", "."),
791
+ initialdir=cntlr.config.setdefault("loadableOIMFileDir", "."),
733
792
  filetypes=[(_("JSON file .json"), "*.json"), (_("CSV file .csv"), "*.csv"), (_("XLSX file .xlsx"), "*.xlsx")],
734
793
  defaultextension=".json",
735
794
  ) # type: ignore[no-untyped-call]
736
795
  if not isinstance(oimFile, str):
796
+ # User cancelled file dialog.
737
797
  return
738
798
 
739
799
  cntlr.config["loadableOIMFileDir"] = os.path.dirname(oimFile)
@@ -606,7 +606,7 @@ def validateXbrlFinally(val: ValidateXbrl, *args: Any, **kwargs: Any) -> None:
606
606
  for context in contextsWithWrongInstantDate:
607
607
  modelXbrl.error("ESEF.2.1.2.inappropriateInstantDate",
608
608
  _("Instant date %(actualValue)s in context %(contextID)s shall be replaced by %(expectedValue)s to ensure a better comparability between the facts."),
609
- modelObject=contextsWithWrongInstantDate, actualValue=context.instantDate, expectedValue=context.instantDate - timedelta(days=1), contextID=context.id)
609
+ modelObject=context, actualValue=context.instantDate, expectedValue=context.instantDate - timedelta(days=1), contextID=context.id)
610
610
 
611
611
  # identify unique contexts and units
612
612
  mapContext = {}
@@ -9,16 +9,22 @@ import regex as re
9
9
  from collections import defaultdict
10
10
  from dataclasses import dataclass
11
11
 
12
- from arelle.ModelInstanceObject import ModelUnit, ModelContext, ModelFact
12
+ from arelle.FunctionIxt import ixtNamespaces
13
+ from arelle.ModelInstanceObject import ModelUnit, ModelContext, ModelFact, ModelInlineFootnote
13
14
  from arelle.ModelValue import QName
14
15
  from arelle.ModelXbrl import ModelXbrl
15
16
  from arelle.utils.PluginData import PluginData
16
17
  from arelle.XmlValidate import lexicalPatterns
17
18
 
18
-
19
19
  XBRLI_IDENTIFIER_PATTERN = re.compile(r"^(?!00)\d{8}$")
20
20
  XBRLI_IDENTIFIER_SCHEMA = 'http://www.kvk.nl/kvk-id'
21
21
 
22
+ DISALLOWED_IXT_NAMESPACES = frozenset((
23
+ ixtNamespaces["ixt v1"],
24
+ ixtNamespaces["ixt v2"],
25
+ ixtNamespaces["ixt v3"],
26
+ ))
27
+
22
28
  @dataclass
23
29
  class PluginValidationDataExtension(PluginData):
24
30
  chamberOfCommerceRegistrationNumberQn: QName
@@ -42,6 +48,9 @@ class PluginValidationDataExtension(PluginData):
42
48
  _contextsWithSegments: list[ModelContext | None] | None = None
43
49
  _entityIdentifiers: set[tuple[str, str]] | None = None
44
50
  _factsByDocument: dict[str, list[ModelFact]] | None = None
51
+ _factLangs: set[str] | None = None
52
+ _noMatchLangFootnotes: set[ModelInlineFootnote] | None = None
53
+ _orphanedFootnotes: set[ModelInlineFootnote] | None = None
45
54
  _unitsByDocument: dict[str, list[ModelUnit]] | None = None
46
55
 
47
56
  def contextsByDocument(self, modelXbrl: ModelXbrl) -> dict[str, list[ModelContext]]:
@@ -80,6 +89,23 @@ class PluginValidationDataExtension(PluginData):
80
89
  self._contextsWithPeriodTimeZone = contextsWithPeriodTimeZone
81
90
  self._contextsWithSegments = contextsWithSegments
82
91
 
92
+ def checkFootnote(self, modelXbrl: ModelXbrl) -> None:
93
+ factLangs = self.factLangs(modelXbrl)
94
+ footnotesRelationshipSet = modelXbrl.relationshipSet("XBRL-footnotes")
95
+ orphanedFootnotes = set()
96
+ noMatchLangFootnotes = set()
97
+ for elts in modelXbrl.ixdsEltById.values(): # type: ignore[attr-defined]
98
+ for elt in elts:
99
+ if isinstance(elt, ModelInlineFootnote):
100
+ if elt.textValue is not None:
101
+ if not any(isinstance(rel.fromModelObject, ModelFact)
102
+ for rel in footnotesRelationshipSet.toModelObject(elt)):
103
+ orphanedFootnotes.add(elt)
104
+ if not elt.xmlLang in factLangs:
105
+ noMatchLangFootnotes.add(elt)
106
+ self._noMatchLangFootnotes = noMatchLangFootnotes
107
+ self._orphanedFootnotes = orphanedFootnotes
108
+
83
109
  def entityIdentifiersInDocument(self, modelXbrl: ModelXbrl) -> set[tuple[str, str]]:
84
110
  if self._entityIdentifiers is not None:
85
111
  return self._entityIdentifiers
@@ -95,6 +121,16 @@ class PluginValidationDataExtension(PluginData):
95
121
  self._factsByDocument = dict(factsByDocument)
96
122
  return self._factsByDocument
97
123
 
124
+ def factLangs(self, modelXbrl: ModelXbrl) -> set[str]:
125
+ if self._factLangs is not None:
126
+ return self._factLangs
127
+ factLangs = set()
128
+ for fact in modelXbrl.facts:
129
+ if fact is not None:
130
+ factLangs.add(fact.xmlLang)
131
+ self._factLangs = factLangs
132
+ return self._factLangs
133
+
98
134
  def getContextsWithImproperContent(self, modelXbrl: ModelXbrl) -> list[ModelContext | None]:
99
135
  if self._contextsWithImproperContent is None:
100
136
  self.checkContexts(self.contextsByDocument(modelXbrl))
@@ -119,6 +155,18 @@ class PluginValidationDataExtension(PluginData):
119
155
  assert(self._contextsWithSegments is not None)
120
156
  return self._contextsWithSegments
121
157
 
158
+ def getNoMatchLangFootnotes(self, modelXbrl: ModelXbrl) -> set[ModelInlineFootnote]:
159
+ if self._noMatchLangFootnotes is None:
160
+ self.checkFootnote(modelXbrl)
161
+ assert(self._noMatchLangFootnotes is not None)
162
+ return self._noMatchLangFootnotes
163
+
164
+ def getOrphanedFootnotes(self, modelXbrl: ModelXbrl) -> set[ModelInlineFootnote]:
165
+ if self._orphanedFootnotes is None:
166
+ self.checkFootnote(modelXbrl)
167
+ assert(self._orphanedFootnotes is not None)
168
+ return self._orphanedFootnotes
169
+
122
170
  def unitsByDocument(self, modelXbrl: ModelXbrl) -> dict[str, list[ModelUnit]]:
123
171
  if self._unitsByDocument is not None:
124
172
  return self._unitsByDocument
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
 
6
6
  from datetime import date, timedelta
7
7
 
8
+ from arelle.ModelInstanceObject import ModelInlineFact
8
9
  from arelle.XmlValidateConst import VALID
9
10
  from dateutil import relativedelta
10
11
  from collections.abc import Iterable
@@ -21,7 +22,7 @@ from arelle.utils.PluginHooks import ValidationHook
21
22
  from arelle.utils.validate.Decorator import validation
22
23
  from arelle.utils.validate.Validation import Validation
23
24
  from ..DisclosureSystems import DISCLOSURE_SYSTEM_NL_INLINE_2024
24
- from ..PluginValidationDataExtension import PluginValidationDataExtension, XBRLI_IDENTIFIER_PATTERN, XBRLI_IDENTIFIER_SCHEMA
25
+ from ..PluginValidationDataExtension import PluginValidationDataExtension, XBRLI_IDENTIFIER_PATTERN, XBRLI_IDENTIFIER_SCHEMA, DISALLOWED_IXT_NAMESPACES
25
26
 
26
27
  if TYPE_CHECKING:
27
28
  from arelle.ModelXbrl import ModelXbrl
@@ -269,3 +270,78 @@ def rule_nl_kvk_3_2_1_1 (
269
270
  msg=_('Precision should not be used on numeric facts.'),
270
271
  modelObject = factsWithPrecision
271
272
  )
273
+
274
+
275
+ @validation(
276
+ hook=ValidationHook.XBRL_FINALLY,
277
+ disclosureSystems=[
278
+ DISCLOSURE_SYSTEM_NL_INLINE_2024
279
+ ],
280
+ )
281
+ def rule_nl_kvk_3_2_3_1 (
282
+ pluginData: PluginValidationDataExtension,
283
+ val: ValidateXbrl,
284
+ *args: Any,
285
+ **kwargs: Any,
286
+ ) -> Iterable[Validation]:
287
+ """
288
+ NL-KVK.3.2.3.1: Transformation Registry 4 or newer are allowed. Everything else is prohibited.
289
+ """
290
+ transformRegistryErrors = []
291
+ for fact in val.modelXbrl.facts:
292
+ if isinstance(fact, ModelInlineFact):
293
+ if fact.format is not None and fact.format.namespaceURI in DISALLOWED_IXT_NAMESPACES:
294
+ transformRegistryErrors.append(fact)
295
+ if len(transformRegistryErrors) >0:
296
+ yield Validation.error(
297
+ codes='NL.NL-KVK.3.2.3.1.incorrectTransformationRuleApplied',
298
+ msg=_('Transformation Registry 4 or newer are allowed. Everything else is prohibited.'),
299
+ modelObject = transformRegistryErrors
300
+ )
301
+
302
+
303
+ @validation(
304
+ hook=ValidationHook.XBRL_FINALLY,
305
+ disclosureSystems=[
306
+ DISCLOSURE_SYSTEM_NL_INLINE_2024
307
+ ],
308
+ )
309
+ def rule_nl_kvk_3_3_1_1 (
310
+ pluginData: PluginValidationDataExtension,
311
+ val: ValidateXbrl,
312
+ *args: Any,
313
+ **kwargs: Any,
314
+ ) -> Iterable[Validation]:
315
+ """
316
+ NL-KVK.3.3.1.1: Ensure that every nonempty <link:footnote> element is associated with at least one fact in the XBRL document.
317
+ """
318
+ orphanedFootnotes = pluginData.getOrphanedFootnotes(val.modelXbrl)
319
+ if len(orphanedFootnotes) >0:
320
+ yield Validation.error(
321
+ codes='NL.NL-KVK.3.3.1.1.unusedFootnote',
322
+ msg=_('Ensure that every nonempty <link:footnote> element is associated with at least one fact in the XBRL document.'),
323
+ modelObject = orphanedFootnotes
324
+ )
325
+
326
+ @validation(
327
+ hook=ValidationHook.XBRL_FINALLY,
328
+ disclosureSystems=[
329
+ DISCLOSURE_SYSTEM_NL_INLINE_2024
330
+ ],
331
+ )
332
+ def rule_nl_kvk_3_3_1_2 (
333
+ pluginData: PluginValidationDataExtension,
334
+ val: ValidateXbrl,
335
+ *args: Any,
336
+ **kwargs: Any,
337
+ ) -> Iterable[Validation]:
338
+ """
339
+ NL-KVK.3.3.1.2: The xml:lang attribute of each footnote matches the language of at least one textual fact.
340
+ """
341
+ noMatchLangFootnotes = pluginData.getNoMatchLangFootnotes(val.modelXbrl)
342
+ if len(noMatchLangFootnotes) >0:
343
+ yield Validation.error(
344
+ codes='NL.NL-KVK.3.3.1.2.footnoteInLanguagesOtherThanLanguageOfContentOfAnyTextualFact',
345
+ msg=_('The xml:lang attribute of each footnote matches the language of at least one textual fact.'),
346
+ modelObject = noMatchLangFootnotes
347
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arelle-release
3
- Version: 2.37.6
3
+ Version: 2.37.8
4
4
  Summary: An open source XBRL platform.
5
5
  Author-email: "arelle.org" <support@arelle.org>
6
6
  License: Apache-2.0
@@ -1,7 +1,7 @@
1
1
  arelle/Aspect.py,sha256=Pn9I91D1os1RTVj6htuxTfRzVMhmVDtrbKvV_zy9xMI,5470
2
2
  arelle/BetaFeatures.py,sha256=T_tPac-FiozHyYLCemt0RoHJ1JahUE71L-0tHmIRKpE,858
3
3
  arelle/Cntlr.py,sha256=sf5Xe19t5E0wKzhdlXl1p5r6gMtCmPhYAi8RVY0jz7Y,30449
4
- arelle/CntlrCmdLine.py,sha256=50acj2edwwIxVjMnQ3MdxuCq1B89g6peCogPi1Ox_OI,94190
4
+ arelle/CntlrCmdLine.py,sha256=-V3daLdYaDRqLTnJ8VYHkaYfvFEZURodCuh1kqNcPO0,94194
5
5
  arelle/CntlrComServer.py,sha256=h1KPf31uMbErpxTZn_iklDqUMGFgQnjZkFkFjd8gtLQ,1888
6
6
  arelle/CntlrProfiler.py,sha256=2VQJudiUhxryVypxjODx2ccP1-n60icTiWs5lSEokhQ,972
7
7
  arelle/CntlrQuickBooks.py,sha256=BMqd5nkNQOZyNFPefkTeWUUDCYNS6BQavaG8k1Lepu4,31543
@@ -123,7 +123,7 @@ arelle/XmlValidateConst.py,sha256=U_wN0Q-nWKwf6dKJtcu_83FXPn9c6P8JjzGA5b0w7P0,33
123
123
  arelle/XmlValidateParticles.py,sha256=Mn6vhFl0ZKC_vag1mBwn1rH_x2jmlusJYqOOuxFPO2k,9231
124
124
  arelle/XmlValidateSchema.py,sha256=6frtZOc1Yrx_5yYF6V6oHbScnglWrVbWr6xW4EHtLQI,7428
125
125
  arelle/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
126
- arelle/_version.py,sha256=zRLSwKHPn00qQt28e4Gt4FsbXPxaiSo4UQK7riBk8OU,513
126
+ arelle/_version.py,sha256=cAF-eK-alaFm1NSbJP5rO2cSf3Bdy0OV8kAVRdfoiWI,513
127
127
  arelle/typing.py,sha256=Ct5lrNKRow_o9CraMEXNza8nFsJ_iGIKoUeGfPs2dxI,1084
128
128
  arelle/api/Session.py,sha256=O8zpg7MJys9uxwwHf8OsSlZxpPdq7A3ONyY39Q4A3Kc,6218
129
129
  arelle/archive/CustomLogger.py,sha256=v_JXOCQLDZcfaFWzxC9FRcEf9tQi4rCI4Sx7jCuAVQI,1231
@@ -294,7 +294,7 @@ arelle/model/CommentBase.py,sha256=NtC2lFd9Mt1y7kzWwrpvexwqBdfSe1nvGFiIJeio3rU,1
294
294
  arelle/model/ElementBase.py,sha256=pZX836d4-s-OvzPMUusvEDezI9D_6YKO7_j6iDcUXm4,404
295
295
  arelle/model/PIBase.py,sha256=easZ3pKXJ5wq3NFB2pDtBeXNDcjwMAXylpXz6mnumOg,257
296
296
  arelle/model/__init__.py,sha256=RLmC1rTus3T_2Vvnu3yHtdw1r0wrZCHZoqxe8BLg_wE,595
297
- arelle/oim/Load.py,sha256=2e9-rIZwGwjTlLILadXuQ1jIuPtKbXd2538owHu3-EE,181900
297
+ arelle/oim/Load.py,sha256=TX5lA0MsN1Vig0A6ozlY-eOa9z0OaAxBcsICu_jrlk0,182158
298
298
  arelle/oim/Validate.py,sha256=IaBClr2KYMiVC_GKYy4_A9gF7hcnm-lxXpQrDCqIWGs,9012
299
299
  arelle/oim/xml/Save.py,sha256=MdaJiGcEo4nbQCX9sRgWfVIoxp6fd2N-wuLiDAS9D-I,607
300
300
  arelle/packages/PackageConst.py,sha256=iIIF-Ic8zlMPiiCq3PcV57aWci6ispBtilSG4W7ZW4U,121
@@ -323,7 +323,7 @@ arelle/plugin/saveCHComponentFile.py,sha256=phW-n96P0BWONLqX3wBcuNcDa_IQ8QVfFWKL
323
323
  arelle/plugin/saveDTS.py,sha256=D8hfFiM9Gc9LE7ZV0-pDvOHeU7Y5ebOzZx_nXzoZrl8,3216
324
324
  arelle/plugin/saveHtmlEBAtables.py,sha256=OITN4ftI_atUO5NvrDJDKUc0CAk67GY68Jl16xDBXiI,10961
325
325
  arelle/plugin/saveLoadableExcel.py,sha256=ZPYB6injFabav0dRzgS7adCFzfHkwtftjl3PfBjijuU,20539
326
- arelle/plugin/saveLoadableOIM.py,sha256=onT61QRygFCMGS7xTZHJz9WSo1V5PyqKQRtoTBf7GDE,38180
326
+ arelle/plugin/saveLoadableOIM.py,sha256=lJ2qZ6ZMxCCAkz9wY0cucOhk8wTZulNlSlXH3qCKy2Q,40633
327
327
  arelle/plugin/saveSKOS.py,sha256=7Z1Qedf83zMo9EbigKkxNpiMjzkTYQvLEnwWMObLc1Y,12465
328
328
  arelle/plugin/saveSampleInstance.py,sha256=w66Nd4He8yIK4Il_M9qnelpeUJ_6cBBe5q01DanOxEA,23823
329
329
  arelle/plugin/streamingExtensions.py,sha256=9W4iQ1u8LFn7-ZEfbqBtuwKiZj-aq8FUXNmj5VawRXQ,47619
@@ -370,7 +370,7 @@ arelle/plugin/validate/ESEF/ESEF_2021/ValidateXbrlFinally.py,sha256=AGjPhegfY9hg
370
370
  arelle/plugin/validate/ESEF/ESEF_2021/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
371
371
  arelle/plugin/validate/ESEF/ESEF_Current/DTS.py,sha256=epp-PBh1NJzQqgxUE6C468HmoDc2w3j54rMwfiOAry4,29334
372
372
  arelle/plugin/validate/ESEF/ESEF_Current/Image.py,sha256=w36sCTy8QbsuKABjkK6PTWce2A4zFN_rMnEM2wi5WEc,11364
373
- arelle/plugin/validate/ESEF/ESEF_Current/ValidateXbrlFinally.py,sha256=F7UahhDQ4jieUyPKqzLCAld8e1YbSjMmZrEciAUXTH4,73130
373
+ arelle/plugin/validate/ESEF/ESEF_Current/ValidateXbrlFinally.py,sha256=z0ZAjDbt6jhGmcSSjfaW-MiR8jO8_ovVGXfGEk43060,73109
374
374
  arelle/plugin/validate/ESEF/ESEF_Current/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
375
375
  arelle/plugin/validate/ESEF/resources/authority-validations.json,sha256=JriLZ45KmUYlQiPbXJCAahobqdrst64Ay77bofZhB5Q,14940
376
376
  arelle/plugin/validate/ESEF/resources/config.xml,sha256=t3STU_-QYM7Ay8YwZRPapnohiWVWhjfr4L2Rjx9xN9U,3902
@@ -378,7 +378,7 @@ arelle/plugin/validate/FERC/__init__.py,sha256=V4fXcFKBsjFFPs9_1NhvDjWpEQCoQM0tR
378
378
  arelle/plugin/validate/FERC/config.xml,sha256=bn9b8eCqJA1J62rYq1Nz85wJrMGAahVmmnIUQZyerjo,1919
379
379
  arelle/plugin/validate/FERC/resources/ferc-utr.xml,sha256=OCRj9IUpdXATCBXKbB71apYx9kxcNtZW-Hq4s-avsRY,2663
380
380
  arelle/plugin/validate/NL/DisclosureSystems.py,sha256=kTjpxkgwn58wHCbaLRBInirOy-2cpK9MLWEFJ_193y4,180
381
- arelle/plugin/validate/NL/PluginValidationDataExtension.py,sha256=WuCXeiCYlDWDrQ--lRkTVTTuD_KTemgYsiWMxs6psxM,6259
381
+ arelle/plugin/validate/NL/PluginValidationDataExtension.py,sha256=3SaQ85Hd8APZM994Xcl876Wy_DgoY_y2UAm_e3JfdoY,8434
382
382
  arelle/plugin/validate/NL/ValidationPluginExtension.py,sha256=8QL0_FL3kT-pMYqZxRI0BTN6ToJeU-wd800y_7fY3wM,15225
383
383
  arelle/plugin/validate/NL/__init__.py,sha256=99uMv4ESHwyJqA-Xq_hBvHygm0BQ3NxcmAJnVYUkSgg,3104
384
384
  arelle/plugin/validate/NL/resources/config.xml,sha256=i_ns2wHmQYjhkRItevRR8tzfkl31ASfbWlc5t6pDB-w,1117
@@ -387,7 +387,7 @@ arelle/plugin/validate/NL/rules/br_kvk.py,sha256=0SwKieWzTDm3YMsXPS6zTdgbk7_Z9Cz
387
387
  arelle/plugin/validate/NL/rules/fg_nl.py,sha256=4Puq5wAjtK_iNd4wisH_R0Z_EKJ7MT2OCai5g4t1MPE,10714
388
388
  arelle/plugin/validate/NL/rules/fr_kvk.py,sha256=-_BLeWGoZ_f56p5VO4X40S45Ny3Ej-WK6Srei1KVSxU,8170
389
389
  arelle/plugin/validate/NL/rules/fr_nl.py,sha256=-M1WtXp06khhtkfOVPCa-b8UbC281gk4YfDhvtAVlnI,31424
390
- arelle/plugin/validate/NL/rules/nl_kvk.py,sha256=sqzHczrr5jLgb5ZHdMWBc2urH_eNb-eNLxU8UnGZyQs,9082
390
+ arelle/plugin/validate/NL/rules/nl_kvk.py,sha256=OqckHX2Nxv-pvSe3ieai5P-Czb-cBOuNI04BJaCpXnQ,11822
391
391
  arelle/plugin/validate/ROS/DisclosureSystems.py,sha256=rJ81mwQDYTi6JecFZ_zhqjjz3VNQRgjHNSh0wcQWAQE,18
392
392
  arelle/plugin/validate/ROS/PluginValidationDataExtension.py,sha256=IV7ILhNvgKwQXqbpSA6HRNt9kEnejCyMADI3wyyIgk0,4036
393
393
  arelle/plugin/validate/ROS/ValidationPluginExtension.py,sha256=FBhEp8t396vGdvCbMEimfcxmGiGnhXMen-yVLWnkFaI,758
@@ -707,7 +707,7 @@ arelle/utils/validate/ValidationPlugin.py,sha256=_WeRPXZUTCcSN3FLbFwiAe_2pAUTxZZ
707
707
  arelle/utils/validate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
708
708
  arelle/webserver/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
709
709
  arelle/webserver/bottle.py,sha256=P-JECd9MCTNcxCnKoDUvGcoi03ezYVOgoWgv2_uH-6M,362
710
- arelle_release-2.37.6.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
710
+ arelle_release-2.37.8.dist-info/licenses/LICENSE.md,sha256=Q0tn6q0VUbr-NM8916513NCIG8MNzo24Ev-sxMUBRZc,3959
711
711
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
712
712
  tests/integration_tests/download_cache.py,sha256=jVMIVICsZjcVc9DCPPu3fCjF9_cWSS3tqSynhFs3oAM,4097
713
713
  tests/integration_tests/integration_test_util.py,sha256=H7mncbv0T9ZeVyrtk9Hohe3k6jgcYykHkt-LGE-Q9aQ,10270
@@ -762,7 +762,7 @@ tests/integration_tests/validation/conformance_suite_configurations/kvk_nt16.py,
762
762
  tests/integration_tests/validation/conformance_suite_configurations/kvk_nt17.py,sha256=lmEZonthFm0YKFmp1dwXtdJ2T7txUeSpL4mbAo8fl4Y,1292
763
763
  tests/integration_tests/validation/conformance_suite_configurations/kvk_nt18.py,sha256=EG2RQVkvFENhzUF3fl3QvDnH7ZPYS1n1Fo8bhfmSczM,1205
764
764
  tests/integration_tests/validation/conformance_suite_configurations/kvk_nt19.py,sha256=FAzf9RhRmn_8yowpplJho2zEspX9FxJiVq8SjZT3Dsc,1199
765
- tests/integration_tests/validation/conformance_suite_configurations/nl_inline_2024.py,sha256=CBfDHrQ59S9MLxTSN07bbIz8kAZGtb551RaQhyfbK3I,10279
765
+ tests/integration_tests/validation/conformance_suite_configurations/nl_inline_2024.py,sha256=hzPHrwrbkrHCx2hkYk7z97BZHEaxgR7sSkrF46VoemI,10313
766
766
  tests/integration_tests/validation/conformance_suite_configurations/nl_nt16.py,sha256=O_LFVBZPkjxmbrU7_C7VTLtrdoCUx2bYXOXw6_MlRtQ,846
767
767
  tests/integration_tests/validation/conformance_suite_configurations/nl_nt17.py,sha256=aTN3Ez6lPsZsuypHZP84DneOtYxUZSjUiGypHy6ofHQ,846
768
768
  tests/integration_tests/validation/conformance_suite_configurations/nl_nt18.py,sha256=sqHLjrHc95dTu0guTgKkphaKM1zNfKGnN4GKkZDLzeU,845
@@ -1556,8 +1556,8 @@ tests/unit_tests/arelle/oim/test_load.py,sha256=NxiUauQwJVfWAHbbpsMHGSU2d3Br8Pki
1556
1556
  tests/unit_tests/arelle/plugin/test_plugin_imports.py,sha256=bdhIs9frAnFsdGU113yBk09_jis-z43dwUItMFYuSYM,1064
1557
1557
  tests/unit_tests/arelle/plugin/validate/ESEF/ESEF_Current/test_validate_css_url.py,sha256=XHABmejQt7RlZ0udh7v42f2Xb2STGk_fSaIaJ9i2xo0,878
1558
1558
  tests/unit_tests/arelle/utils/validate/test_decorator.py,sha256=ZS8FqIY1g-2FCbjF4UYm609dwViax6qBMRJSi0vfuhY,2482
1559
- arelle_release-2.37.6.dist-info/METADATA,sha256=JUHetf5mfMb0WG0PzSwUmF2m112VfqSngeCEtAjhXvA,9064
1560
- arelle_release-2.37.6.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
1561
- arelle_release-2.37.6.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
1562
- arelle_release-2.37.6.dist-info/top_level.txt,sha256=ZYmYGmhW5Jvo3vJ4iXBZPUI29LvYhntom04w90esJvU,13
1563
- arelle_release-2.37.6.dist-info/RECORD,,
1559
+ arelle_release-2.37.8.dist-info/METADATA,sha256=vVYSyHCG28r9Y7qYSKyC9E5188Rza7T9GchVet2RJDw,9064
1560
+ arelle_release-2.37.8.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
1561
+ arelle_release-2.37.8.dist-info/entry_points.txt,sha256=Uj5niwfwVsx3vaQ3fYj8hrZ1xpfCJyTUA09tYKWbzpo,111
1562
+ arelle_release-2.37.8.dist-info/top_level.txt,sha256=ZYmYGmhW5Jvo3vJ4iXBZPUI29LvYhntom04w90esJvU,13
1563
+ arelle_release-2.37.8.dist-info/RECORD,,
@@ -19,15 +19,12 @@ config = ConformanceSuiteConfig(
19
19
  ],
20
20
  expected_failure_ids=frozenset([
21
21
  # Not Implemented
22
- 'conformance-suite-2024-sbr-domein-handelsregister/tests/G3-2-3_1/index.xml:TC3_invalid',
23
- 'conformance-suite-2024-sbr-domein-handelsregister/tests/G3-2-3_1/index.xml:TC4_invalid',
24
22
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G3-2-4_2/index.xml:TC4_invalid',
25
23
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G3-2-7_1/index.xml:TC4_invalid',
26
24
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G3-2-7_1/index.xml:TC5_invalid',
27
25
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G3-2-7_1/index.xml:TC6_invalid',
28
26
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G3-2-7_1/index.xml:TC7_invalid',
29
- 'conformance-suite-2024-sbr-domein-handelsregister/tests/G3-3-1_1/index.xml:TC2_invalid',
30
- 'conformance-suite-2024-sbr-domein-handelsregister/tests/G3-3-1_2/index.xml:TC3_invalid',
27
+ 'conformance-suite-2024-sbr-domein-handelsregister/tests/G3-3-1_2/index.xml:TC3_invalid', # Expects an error code with a preceding double quote. G3-3-1_3 expects the same code without the typo.
31
28
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G3-3-1_3/index.xml:TC2_invalid',
32
29
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G3-4-1_1/index.xml:TC2_invalid',
33
30
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G3-4-1_2/index.xml:TC2_invalid',
@@ -61,6 +58,7 @@ config = ConformanceSuiteConfig(
61
58
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-1-2_2/index.xml:TC2_invalid',
62
59
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-2-0_1/index.xml:TC2_invalid',
63
60
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-2-0_2/index.xml:TC2_invalid',
61
+ 'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-2-1_1/index.xml:TC2_invalid', # Expects scenarioNotUsedInExtensionTaxonomy and segmentUsed errors. scenarioNotUsedInExtensionTaxonomy not yet implemented.
64
62
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-2-2_2/index.xml:TC2_invalid',
65
63
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-2-3_1/index.xml:TC2_invalid',
66
64
  'conformance-suite-2024-sbr-domein-handelsregister/tests/G4-3-1_1/index.xml:TC2_invalid',
@@ -114,5 +112,5 @@ config = ConformanceSuiteConfig(
114
112
  network_or_cache_required=False,
115
113
  plugins=frozenset({'validate/NL'}),
116
114
  shards=8,
117
- test_case_result_options='match-any',
115
+ test_case_result_options='match-all',
118
116
  )