arelle-release 2.37.12__py3-none-any.whl → 2.37.14__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of arelle-release might be problematic. Click here for more details.
- arelle/CntlrCmdLine.py +1 -66
- arelle/ModelDtsObject.py +1 -1
- arelle/ModelInstanceObject.py +9 -0
- arelle/ModelTestcaseObject.py +8 -1
- arelle/Validate.py +58 -23
- arelle/ValidateDuplicateFacts.py +4 -10
- arelle/_version.py +2 -2
- arelle/formula/XPathParser.py +9 -3
- arelle/packages/report/ReportPackage.py +7 -3
- arelle/packages/report/ReportPackageValidator.py +4 -3
- arelle/plugin/inlineXbrlDocumentSet.py +1 -1
- arelle/plugin/validate/DBA/PluginValidationDataExtension.py +9 -1
- arelle/plugin/validate/DBA/ValidationPluginExtension.py +4 -2
- arelle/plugin/validate/DBA/rules/fr.py +30 -32
- arelle/plugin/validate/DBA/rules/tm.py +22 -12
- arelle/plugin/validate/ESEF/ESEF_2021/ValidateXbrlFinally.py +3 -3
- arelle/plugin/validate/ESEF/ESEF_Current/ValidateXbrlFinally.py +4 -5
- arelle/plugin/validate/NL/PluginValidationDataExtension.py +163 -32
- arelle/plugin/validate/NL/__init__.py +0 -11
- arelle/plugin/validate/NL/rules/nl_kvk.py +322 -12
- arelle/plugin/validate/ROS/rules/ros.py +1 -1
- arelle/typing.py +8 -1
- arelle/utils/EntryPointDetection.py +73 -0
- {arelle_release-2.37.12.dist-info → arelle_release-2.37.14.dist-info}/METADATA +1 -1
- {arelle_release-2.37.12.dist-info → arelle_release-2.37.14.dist-info}/RECORD +40 -38
- {arelle_release-2.37.12.dist-info → arelle_release-2.37.14.dist-info}/WHEEL +1 -1
- tests/integration_tests/ui_tests/ArelleGUITest/ArelleGUITest/ArelleGUITest.csproj +1 -1
- tests/integration_tests/validation/conformance_suite_configurations/nl_inline_2024.py +14 -21
- tests/integration_tests/validation/conformance_suite_configurations/xbrl_report_packages_1_0.py +3 -1
- tests/resources/conformance_suites/dba/fr/fr7-invalid.xhtml +1 -1
- tests/resources/conformance_suites/dba/fr/fr83-invalid.xbrl +1 -1
- tests/resources/conformance_suites/dba/fr/fr89-testcase.xml +10 -0
- tests/resources/conformance_suites/dba/fr/fr89-valid.xhtml +6816 -0
- tests/resources/conformance_suites/dba/fr/fr91-invalid.xhtml +1 -2
- tests/resources/conformance_suites/dba/tm/tm29-invalid.xhtml +0 -1
- tests/resources/conformance_suites/dba/tm/tm31-invalid.xhtml +10 -1
- tests/resources/conformance_suites/dba/tr/tr06-invalid.xhtml +1 -1
- {arelle_release-2.37.12.dist-info → arelle_release-2.37.14.dist-info}/entry_points.txt +0 -0
- {arelle_release-2.37.12.dist-info → arelle_release-2.37.14.dist-info}/licenses/LICENSE.md +0 -0
- {arelle_release-2.37.12.dist-info → arelle_release-2.37.14.dist-info}/top_level.txt +0 -0
|
@@ -9,7 +9,7 @@ from arelle.ModelInstanceObject import ModelInlineFact
|
|
|
9
9
|
from arelle.ValidateDuplicateFacts import getDuplicateFactSets
|
|
10
10
|
from arelle.XmlValidateConst import VALID
|
|
11
11
|
from collections.abc import Iterable
|
|
12
|
-
from typing import Any, TYPE_CHECKING
|
|
12
|
+
from typing import Any, cast, TYPE_CHECKING
|
|
13
13
|
|
|
14
14
|
from arelle import XmlUtil
|
|
15
15
|
from arelle.ValidateXbrl import ValidateXbrl
|
|
@@ -17,8 +17,11 @@ from arelle.typing import TypeGetText
|
|
|
17
17
|
from arelle.utils.PluginHooks import ValidationHook
|
|
18
18
|
from arelle.utils.validate.Decorator import validation
|
|
19
19
|
from arelle.utils.validate.Validation import Validation
|
|
20
|
+
from arelle.ValidateDuplicateFacts import getHashEquivalentFactGroups, getAspectEqualFacts
|
|
21
|
+
from arelle.utils.validate.ValidationUtil import etreeIterWithDepth
|
|
20
22
|
from ..DisclosureSystems import DISCLOSURE_SYSTEM_NL_INLINE_2024
|
|
21
|
-
from ..PluginValidationDataExtension import PluginValidationDataExtension, XBRLI_IDENTIFIER_PATTERN,
|
|
23
|
+
from ..PluginValidationDataExtension import (PluginValidationDataExtension, XBRLI_IDENTIFIER_PATTERN,
|
|
24
|
+
XBRLI_IDENTIFIER_SCHEMA, DISALLOWED_IXT_NAMESPACES, ALLOWABLE_LANGUAGES)
|
|
22
25
|
|
|
23
26
|
if TYPE_CHECKING:
|
|
24
27
|
from arelle.ModelXbrl import ModelXbrl
|
|
@@ -356,6 +359,35 @@ def rule_nl_kvk_3_2_4_2 (
|
|
|
356
359
|
)
|
|
357
360
|
|
|
358
361
|
|
|
362
|
+
@validation(
|
|
363
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
364
|
+
disclosureSystems=[
|
|
365
|
+
DISCLOSURE_SYSTEM_NL_INLINE_2024
|
|
366
|
+
],
|
|
367
|
+
)
|
|
368
|
+
def rule_nl_kvk_3_2_7_1 (
|
|
369
|
+
pluginData: PluginValidationDataExtension,
|
|
370
|
+
val: ValidateXbrl,
|
|
371
|
+
*args: Any,
|
|
372
|
+
**kwargs: Any,
|
|
373
|
+
) -> Iterable[Validation]:
|
|
374
|
+
"""
|
|
375
|
+
NL-KVK.3.2.7.1: Ensure that any block-tagged facts of type textBlockItemType are assigned @escape="true",
|
|
376
|
+
while other data types (e.g., xbrli:stringItemType) are assigned @escape="false".
|
|
377
|
+
"""
|
|
378
|
+
improperlyEscapedFacts = []
|
|
379
|
+
for fact in val.modelXbrl.facts:
|
|
380
|
+
if isinstance(fact, ModelInlineFact) and fact.concept is not None and fact.isEscaped != fact.concept.isTextBlock:
|
|
381
|
+
improperlyEscapedFacts.append(fact)
|
|
382
|
+
if len(improperlyEscapedFacts) >0:
|
|
383
|
+
yield Validation.error(
|
|
384
|
+
codes='NL.NL-KVK.3.2.7.1.improperApplicationOfEscapeAttribute',
|
|
385
|
+
msg=_('Ensure that any block-tagged facts of type textBlockItemType are assigned @escape="true",'
|
|
386
|
+
'while other data types (e.g., xbrli:stringItemType) are assigned @escape="false".'),
|
|
387
|
+
modelObject = improperlyEscapedFacts
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
|
|
359
391
|
@validation(
|
|
360
392
|
hook=ValidationHook.XBRL_FINALLY,
|
|
361
393
|
disclosureSystems=[
|
|
@@ -435,27 +467,305 @@ def rule_nl_kvk_3_3_1_3 (
|
|
|
435
467
|
DISCLOSURE_SYSTEM_NL_INLINE_2024
|
|
436
468
|
],
|
|
437
469
|
)
|
|
438
|
-
def
|
|
470
|
+
def rule_nl_kvk_3_4_1_1 (
|
|
439
471
|
pluginData: PluginValidationDataExtension,
|
|
440
472
|
val: ValidateXbrl,
|
|
441
473
|
*args: Any,
|
|
442
474
|
**kwargs: Any,
|
|
443
475
|
) -> Iterable[Validation]:
|
|
444
476
|
"""
|
|
445
|
-
NL-KVK.3.
|
|
477
|
+
NL-KVK.3.4.1.1: ix:tuple element should not be used in the Inline XBRL document.
|
|
446
478
|
"""
|
|
447
|
-
|
|
448
|
-
|
|
479
|
+
tuples = pluginData.getTupleElements(val.modelXbrl)
|
|
480
|
+
if len(tuples) > 0:
|
|
481
|
+
yield Validation.error(
|
|
482
|
+
codes='NL.NL-KVK.3.4.1.1.tupleElementUsed',
|
|
483
|
+
msg=_('ix:tuple element should not be used in the Inline XBRL document.'),
|
|
484
|
+
modelObject=tuples
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
|
|
488
|
+
@validation(
|
|
489
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
490
|
+
disclosureSystems=[
|
|
491
|
+
DISCLOSURE_SYSTEM_NL_INLINE_2024
|
|
492
|
+
],
|
|
493
|
+
)
|
|
494
|
+
def rule_nl_kvk_3_4_1_2 (
|
|
495
|
+
pluginData: PluginValidationDataExtension,
|
|
496
|
+
val: ValidateXbrl,
|
|
497
|
+
*args: Any,
|
|
498
|
+
**kwargs: Any,
|
|
499
|
+
) -> Iterable[Validation]:
|
|
500
|
+
"""
|
|
501
|
+
NL-KVK.3.4.1.2: ix:fraction element should not be used in the Inline XBRL document.
|
|
502
|
+
"""
|
|
503
|
+
fractions = pluginData.getFractionElements(val.modelXbrl)
|
|
504
|
+
if len(fractions) > 0:
|
|
505
|
+
yield Validation.error(
|
|
506
|
+
codes='NL.NL-KVK.3.4.1.2.fractionElementUsed',
|
|
507
|
+
msg=_('ix:fraction element should not be used in the Inline XBRL document.'),
|
|
508
|
+
modelObject=fractions
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
@validation(
|
|
513
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
514
|
+
disclosureSystems=[
|
|
515
|
+
DISCLOSURE_SYSTEM_NL_INLINE_2024
|
|
516
|
+
],
|
|
517
|
+
)
|
|
518
|
+
def rule_nl_kvk_3_4_1_3 (
|
|
519
|
+
pluginData: PluginValidationDataExtension,
|
|
520
|
+
val: ValidateXbrl,
|
|
521
|
+
*args: Any,
|
|
522
|
+
**kwargs: Any,
|
|
523
|
+
) -> Iterable[Validation]:
|
|
524
|
+
"""
|
|
525
|
+
NL-KVK.3.4.1.3: The ix:hidden section should not include elements that are eligible for transformation
|
|
526
|
+
according to the latest recommended Transformation Rules Registry.
|
|
527
|
+
"""
|
|
528
|
+
facts = pluginData.getEligibleForTransformHiddenFacts(val.modelXbrl)
|
|
529
|
+
if len(facts) > 0:
|
|
530
|
+
yield Validation.error(
|
|
531
|
+
codes='NL.NL-KVK.3.4.1.3.transformableElementIncludedInHiddenSection',
|
|
532
|
+
msg=_('The ix:hidden section should not include elements that are eligible for transformation'
|
|
533
|
+
'according to the latest recommended Transformation Rules Registry.'),
|
|
534
|
+
modelObject=facts
|
|
535
|
+
)
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
@validation(
|
|
539
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
540
|
+
disclosureSystems=[
|
|
541
|
+
DISCLOSURE_SYSTEM_NL_INLINE_2024
|
|
542
|
+
],
|
|
543
|
+
)
|
|
544
|
+
def rule_nl_kvk_3_4_1_4 (
|
|
545
|
+
pluginData: PluginValidationDataExtension,
|
|
546
|
+
val: ValidateXbrl,
|
|
547
|
+
*args: Any,
|
|
548
|
+
**kwargs: Any,
|
|
549
|
+
) -> Iterable[Validation]:
|
|
550
|
+
"""
|
|
551
|
+
NL-KVK.3.4.1.4: ix:hidden section should not contain a fact whose @id attribute is not applied on any ix-hidden style.
|
|
552
|
+
"""
|
|
553
|
+
facts = pluginData.getRequiredToDisplayFacts(val.modelXbrl)
|
|
554
|
+
if len(facts) > 0:
|
|
555
|
+
yield Validation.error(
|
|
556
|
+
codes='NL.NL-KVK.3.4.1.4.factInHiddenSectionNotInReport',
|
|
557
|
+
msg=_('ix:hidden section should not contain a fact whose @id attribute is not applied on any -ix-hidden style.'),
|
|
558
|
+
modelObject=facts
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
@validation(
|
|
563
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
564
|
+
disclosureSystems=[
|
|
565
|
+
DISCLOSURE_SYSTEM_NL_INLINE_2024
|
|
566
|
+
],
|
|
567
|
+
)
|
|
568
|
+
def rule_nl_kvk_3_4_1_5 (
|
|
569
|
+
pluginData: PluginValidationDataExtension,
|
|
570
|
+
val: ValidateXbrl,
|
|
571
|
+
*args: Any,
|
|
572
|
+
**kwargs: Any,
|
|
573
|
+
) -> Iterable[Validation]:
|
|
574
|
+
"""
|
|
575
|
+
NL-KVK.3.4.1.5: ix:hidden section should not contain a fact whose @id attribute is not applied on any ix-hidden style.
|
|
576
|
+
"""
|
|
577
|
+
facts = pluginData.getHiddenFactsOutsideHiddenSection(val.modelXbrl)
|
|
578
|
+
if len(facts) > 0:
|
|
579
|
+
yield Validation.error(
|
|
580
|
+
codes='NL.NL-KVK.3.4.1.5.kvkIxHiddenStyleNotLinkingFactInHiddenSection',
|
|
581
|
+
msg=_('Review for -ix-hidden style identifies @id attribute of a fact that is not in ix:hidden section'),
|
|
582
|
+
modelObject=facts
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
@validation(
|
|
587
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
588
|
+
disclosureSystems=[
|
|
589
|
+
DISCLOSURE_SYSTEM_NL_INLINE_2024
|
|
590
|
+
],
|
|
591
|
+
)
|
|
592
|
+
def rule_nl_kvk_3_5_2_1(
|
|
593
|
+
pluginData: PluginValidationDataExtension,
|
|
594
|
+
val: ValidateXbrl,
|
|
595
|
+
*args: Any,
|
|
596
|
+
**kwargs: Any,
|
|
597
|
+
) -> Iterable[Validation]:
|
|
598
|
+
"""
|
|
599
|
+
NL-KVK.3.5.2.1: Each tagged text fact MUST have the ‘xml:lang’ attribute assigned or inherited.
|
|
600
|
+
"""
|
|
601
|
+
factsWithoutLang = []
|
|
449
602
|
for fact in val.modelXbrl.facts:
|
|
450
603
|
if (fact is not None and
|
|
451
604
|
fact.concept is not None and
|
|
452
605
|
fact.concept.type is not None and
|
|
453
606
|
fact.concept.type.isOimTextFactType and
|
|
454
|
-
fact.xmlLang
|
|
455
|
-
|
|
456
|
-
if len(
|
|
607
|
+
not fact.xmlLang):
|
|
608
|
+
factsWithoutLang.append(fact)
|
|
609
|
+
if len(factsWithoutLang) > 0:
|
|
457
610
|
yield Validation.error(
|
|
458
|
-
codes='NL.NL-KVK.3.5.2.
|
|
459
|
-
msg=_('
|
|
460
|
-
modelObject=
|
|
611
|
+
codes='NL.NL-KVK.3.5.2.1.undefinedLanguageForTextFact',
|
|
612
|
+
msg=_('Each tagged text fact MUST have the ‘xml:lang’ attribute assigned or inherited.'),
|
|
613
|
+
modelObject=factsWithoutLang
|
|
461
614
|
)
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
@validation(
|
|
618
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
619
|
+
disclosureSystems=[
|
|
620
|
+
DISCLOSURE_SYSTEM_NL_INLINE_2024
|
|
621
|
+
],
|
|
622
|
+
)
|
|
623
|
+
def rule_nl_kvk_3_5_2_2(
|
|
624
|
+
pluginData: PluginValidationDataExtension,
|
|
625
|
+
val: ValidateXbrl,
|
|
626
|
+
*args: Any,
|
|
627
|
+
**kwargs: Any,
|
|
628
|
+
) -> Iterable[Validation]:
|
|
629
|
+
"""
|
|
630
|
+
NL-KVK.3.5.2.2: All tagged text facts MUST be provided in at least the language of the report.
|
|
631
|
+
"""
|
|
632
|
+
reportXmlLang = pluginData.getReportXmlLang(val.modelXbrl)
|
|
633
|
+
filtered_facts = [f for f in val.modelXbrl.facts if f.concept is not None and
|
|
634
|
+
f.concept.type is not None and
|
|
635
|
+
f.concept.type.isOimTextFactType and
|
|
636
|
+
f.context is not None]
|
|
637
|
+
factGroups = getHashEquivalentFactGroups(filtered_facts)
|
|
638
|
+
for fgroup in factGroups:
|
|
639
|
+
for flist in getAspectEqualFacts(fgroup, includeSingles=True, useLang=False):
|
|
640
|
+
if not any(f.xmlLang == reportXmlLang for f in flist):
|
|
641
|
+
yield Validation.error(
|
|
642
|
+
codes='NL.NL-KVK.3.5.2.2.taggedTextFactOnlyInLanguagesOtherThanLanguageOfAReport',
|
|
643
|
+
msg=_('Tagged text facts MUST be provided in the language of the report.'),
|
|
644
|
+
modelObject=fgroup
|
|
645
|
+
)
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
@validation(
|
|
649
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
650
|
+
disclosureSystems=[
|
|
651
|
+
DISCLOSURE_SYSTEM_NL_INLINE_2024
|
|
652
|
+
],
|
|
653
|
+
)
|
|
654
|
+
def rule_nl_kvk_3_5_2_3(
|
|
655
|
+
pluginData: PluginValidationDataExtension,
|
|
656
|
+
val: ValidateXbrl,
|
|
657
|
+
*args: Any,
|
|
658
|
+
**kwargs: Any,
|
|
659
|
+
) -> Iterable[Validation]:
|
|
660
|
+
"""
|
|
661
|
+
NL-KVK.3.5.2.3: The value of the @xml:lang attribute SHOULD be 'nl' or 'en' or 'de' or 'fr'.
|
|
662
|
+
"""
|
|
663
|
+
badLangsUsed = set()
|
|
664
|
+
for ixdsHtmlRootElt in val.modelXbrl.ixdsHtmlElements:
|
|
665
|
+
for uncast_elt, depth in etreeIterWithDepth(ixdsHtmlRootElt):
|
|
666
|
+
elt = cast(Any, uncast_elt)
|
|
667
|
+
xmlLang = elt.get("{http://www.w3.org/XML/1998/namespace}lang")
|
|
668
|
+
if xmlLang and xmlLang not in ALLOWABLE_LANGUAGES:
|
|
669
|
+
badLangsUsed.add(xmlLang)
|
|
670
|
+
if len(badLangsUsed) > 0:
|
|
671
|
+
yield Validation.warning(
|
|
672
|
+
codes='NL.NL-KVK.3.5.2.3.invalidLanguageAttribute',
|
|
673
|
+
badLangsUsed=', '.join(badLangsUsed),
|
|
674
|
+
msg=_('The lang attribute should use one of the following: \'nl\' or \'en\' or \'de\' or \'fr\'. '
|
|
675
|
+
'The following languages are used incorrectly: %(badLangsUsed)s'),
|
|
676
|
+
)
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
@validation(
|
|
680
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
681
|
+
disclosureSystems=[
|
|
682
|
+
DISCLOSURE_SYSTEM_NL_INLINE_2024
|
|
683
|
+
],
|
|
684
|
+
)
|
|
685
|
+
def rule_nl_kvk_3_6_3_1(
|
|
686
|
+
pluginData: PluginValidationDataExtension,
|
|
687
|
+
val: ValidateXbrl,
|
|
688
|
+
*args: Any,
|
|
689
|
+
**kwargs: Any,
|
|
690
|
+
) -> Iterable[Validation]:
|
|
691
|
+
"""
|
|
692
|
+
NL-KVK.3.6.3.1: The report filename will have the form `{base}-{date}-{lang}.{extension}`.
|
|
693
|
+
The `{base}` component of the filename SHOULD not exceed twenty characters.
|
|
694
|
+
"""
|
|
695
|
+
invalidBasenames = []
|
|
696
|
+
for basename in pluginData.getIxdsDocBasenames(val.modelXbrl):
|
|
697
|
+
filenameParts = pluginData.getFilenameParts(basename)
|
|
698
|
+
if not filenameParts:
|
|
699
|
+
continue # Filename is not formatted correctly enough to determine {base}
|
|
700
|
+
if len(filenameParts.get('base', '')) > 20:
|
|
701
|
+
invalidBasenames.append(basename)
|
|
702
|
+
if len(invalidBasenames) > 0:
|
|
703
|
+
yield Validation.warning(
|
|
704
|
+
codes='NL.NL-KVK.3.6.3.1.baseComponentInDocumentNameExceedsTwentyCharacters',
|
|
705
|
+
invalidBasenames=', '.join(invalidBasenames),
|
|
706
|
+
msg=_('The {base} component of the filename is greater than twenty characters. '
|
|
707
|
+
'The {base} component can either be the KVK number or the legal entity\'s name. '
|
|
708
|
+
'If the legal entity\'s name has been utilized, review to shorten the name to twenty characters or less. '
|
|
709
|
+
'Invalid filenames: %(invalidBasenames)s'))
|
|
710
|
+
|
|
711
|
+
|
|
712
|
+
@validation(
|
|
713
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
714
|
+
disclosureSystems=[
|
|
715
|
+
DISCLOSURE_SYSTEM_NL_INLINE_2024
|
|
716
|
+
],
|
|
717
|
+
)
|
|
718
|
+
def rule_nl_kvk_3_6_3_2(
|
|
719
|
+
pluginData: PluginValidationDataExtension,
|
|
720
|
+
val: ValidateXbrl,
|
|
721
|
+
*args: Any,
|
|
722
|
+
**kwargs: Any,
|
|
723
|
+
) -> Iterable[Validation]:
|
|
724
|
+
"""
|
|
725
|
+
NL-KVK.3.6.3.2: Report filename SHOULD match the {base}-{date}-{lang}.{extension} pattern.
|
|
726
|
+
{extension} MUST be one of the following: html, htm, xhtml.
|
|
727
|
+
"""
|
|
728
|
+
invalidBasenames = []
|
|
729
|
+
for basename in pluginData.getIxdsDocBasenames(val.modelXbrl):
|
|
730
|
+
filenameParts = pluginData.getFilenameParts(basename)
|
|
731
|
+
if not filenameParts:
|
|
732
|
+
invalidBasenames.append(basename)
|
|
733
|
+
if len(invalidBasenames) > 0:
|
|
734
|
+
yield Validation.warning(
|
|
735
|
+
codes='NL.NL-KVK.3.6.3.2.documentNameDoesNotFollowNamingConvention',
|
|
736
|
+
invalidBasenames=', '.join(invalidBasenames),
|
|
737
|
+
msg=_('The filename does not match the naming convention outlined by the KVK. '
|
|
738
|
+
'It is recommended to be in the {base}-{date}-{lang}.{extension} format. '
|
|
739
|
+
'{extension} must be one of the following: html, htm, xhtml. '
|
|
740
|
+
'Review formatting and update as appropriate. '
|
|
741
|
+
'Invalid filenames: %(invalidBasenames)s'))
|
|
742
|
+
|
|
743
|
+
|
|
744
|
+
@validation(
|
|
745
|
+
hook=ValidationHook.XBRL_FINALLY,
|
|
746
|
+
disclosureSystems=[
|
|
747
|
+
DISCLOSURE_SYSTEM_NL_INLINE_2024
|
|
748
|
+
],
|
|
749
|
+
)
|
|
750
|
+
def rule_nl_kvk_3_6_3_3(
|
|
751
|
+
pluginData: PluginValidationDataExtension,
|
|
752
|
+
val: ValidateXbrl,
|
|
753
|
+
*args: Any,
|
|
754
|
+
**kwargs: Any,
|
|
755
|
+
) -> Iterable[Validation]:
|
|
756
|
+
"""
|
|
757
|
+
NL-KVK.3.6.3.3: Report filename MUST only contain allowed characters.
|
|
758
|
+
Filenames can include the following characters: A-Z, a-z, 0-9, underscore ( _ ), period ( . ), hyphen ( - ).
|
|
759
|
+
"""
|
|
760
|
+
invalidBasenames = []
|
|
761
|
+
for basename in pluginData.getIxdsDocBasenames(val.modelXbrl):
|
|
762
|
+
if not pluginData.isFilenameValidCharacters(basename):
|
|
763
|
+
invalidBasenames.append(basename)
|
|
764
|
+
if len(invalidBasenames) > 0:
|
|
765
|
+
yield Validation.error(
|
|
766
|
+
codes='NL.NL-KVK.3.6.3.3.documentFileNameIncludesCharactersNotAllowed',
|
|
767
|
+
invalidBasenames=', '.join(invalidBasenames),
|
|
768
|
+
msg=_('The file name includes characters that are now allowed. '
|
|
769
|
+
'Allowed characters include: A-Z, a-z, 0-9, underscore ( _ ), period ( . ), and hyphen ( - ). '
|
|
770
|
+
'Update filing naming to review unallowed characters. '
|
|
771
|
+
'Invalid filenames: %(invalidBasenames)s'))
|
|
@@ -91,7 +91,7 @@ def rule_main(
|
|
|
91
91
|
if isinstance(elt, ModelInlineFact):
|
|
92
92
|
if elt.format is not None and elt.format.namespaceURI not in TR_NAMESPACES:
|
|
93
93
|
transformRegistryErrors.add(elt)
|
|
94
|
-
if elt.
|
|
94
|
+
if elt.isEscaped:
|
|
95
95
|
modelXbrl.error("ROS.escapedHTML",
|
|
96
96
|
_("Escaped (x)html fact content is not supported: %(element)s"),
|
|
97
97
|
modelObject=elt, element=eltTag)
|
arelle/typing.py
CHANGED
|
@@ -5,7 +5,14 @@ Type hints for Arelle.
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
|
|
7
7
|
from collections.abc import Callable
|
|
8
|
-
from typing import TypedDict, TypeVar # pylint: disable=no-name-in-module
|
|
8
|
+
from typing import Any, TypedDict, TypeVar # pylint: disable=no-name-in-module
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
from typing import assert_type as assert_type
|
|
12
|
+
except ImportError:
|
|
13
|
+
T = TypeVar('T')
|
|
14
|
+
def assert_type(x: T, _: Any, /) -> T:
|
|
15
|
+
return x
|
|
9
16
|
|
|
10
17
|
TypeGetText = Callable[[str], str]
|
|
11
18
|
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from arelle import (
|
|
4
|
+
ModelDocument,
|
|
5
|
+
PluginManager,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def filesourceEntrypointFiles(filesource, entrypointFiles=None, inlineOnly=False):
|
|
10
|
+
if entrypointFiles is None:
|
|
11
|
+
entrypointFiles = []
|
|
12
|
+
if filesource.isArchive:
|
|
13
|
+
if filesource.isTaxonomyPackage: # if archive is also a taxonomy package, activate mappings
|
|
14
|
+
filesource.loadTaxonomyPackageMappings()
|
|
15
|
+
# HF note: a web api request to load a specific file from archive is ignored, is this right?
|
|
16
|
+
del entrypointFiles[:] # clear out archive from entrypointFiles
|
|
17
|
+
if reportPackage := filesource.reportPackage:
|
|
18
|
+
assert isinstance(filesource.basefile, str)
|
|
19
|
+
for report in reportPackage.reports or []:
|
|
20
|
+
if report.isInline:
|
|
21
|
+
reportEntries = [{"file": f} for f in report.fullPathFiles]
|
|
22
|
+
ixdsDiscovered = False
|
|
23
|
+
for pluginXbrlMethod in PluginManager.pluginClassMethods("InlineDocumentSet.Discovery"):
|
|
24
|
+
pluginXbrlMethod(filesource, reportEntries)
|
|
25
|
+
ixdsDiscovered = True
|
|
26
|
+
if not ixdsDiscovered and len(reportEntries) > 1:
|
|
27
|
+
raise RuntimeError(_("Loading error. Inline document set encountered. Enable 'InlineXbrlDocumentSet' plug-in to load this filing: {0}").format(filesource.url))
|
|
28
|
+
entrypointFiles.extend(reportEntries)
|
|
29
|
+
elif not inlineOnly:
|
|
30
|
+
entrypointFiles.append({"file": report.fullPathPrimary})
|
|
31
|
+
else:
|
|
32
|
+
# attempt to find inline XBRL files before instance files, .xhtml before probing others (ESMA)
|
|
33
|
+
urlsByType = {}
|
|
34
|
+
for _archiveFile in (filesource.dir or ()): # .dir might be none if IOerror
|
|
35
|
+
filesource.select(_archiveFile)
|
|
36
|
+
identifiedType = ModelDocument.Type.identify(filesource, filesource.url)
|
|
37
|
+
if identifiedType in (ModelDocument.Type.INSTANCE, ModelDocument.Type.INLINEXBRL, ModelDocument.Type.HTML):
|
|
38
|
+
urlsByType.setdefault(identifiedType, []).append(filesource.url)
|
|
39
|
+
# use inline instances, if any, else non-inline instances
|
|
40
|
+
for identifiedType in ((ModelDocument.Type.INLINEXBRL,) if inlineOnly else (ModelDocument.Type.INLINEXBRL, ModelDocument.Type.INSTANCE)):
|
|
41
|
+
for url in urlsByType.get(identifiedType, []):
|
|
42
|
+
entrypointFiles.append({"file":url})
|
|
43
|
+
if entrypointFiles:
|
|
44
|
+
if identifiedType == ModelDocument.Type.INLINEXBRL:
|
|
45
|
+
for pluginXbrlMethod in PluginManager.pluginClassMethods("InlineDocumentSet.Discovery"):
|
|
46
|
+
pluginXbrlMethod(filesource, entrypointFiles) # group into IXDS if plugin feature is available
|
|
47
|
+
break # found inline (or non-inline) entrypoint files, don't look for any other type
|
|
48
|
+
# for ESEF non-consolidated xhtml documents accept an xhtml entry point
|
|
49
|
+
if not entrypointFiles and not inlineOnly:
|
|
50
|
+
for url in urlsByType.get(ModelDocument.Type.HTML, []):
|
|
51
|
+
entrypointFiles.append({"file":url})
|
|
52
|
+
if not entrypointFiles and filesource.taxonomyPackage is not None:
|
|
53
|
+
for packageEntry in filesource.taxonomyPackage.get('entryPoints', {}).values():
|
|
54
|
+
for _resolvedUrl, remappedUrl, _closest in packageEntry:
|
|
55
|
+
entrypointFiles.append({"file": remappedUrl})
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
elif os.path.isdir(filesource.url):
|
|
59
|
+
del entrypointFiles[:] # clear list
|
|
60
|
+
hasInline = False
|
|
61
|
+
for _file in os.listdir(filesource.url):
|
|
62
|
+
_path = os.path.join(filesource.url, _file)
|
|
63
|
+
if os.path.isfile(_path):
|
|
64
|
+
identifiedType = ModelDocument.Type.identify(filesource, _path)
|
|
65
|
+
if identifiedType == ModelDocument.Type.INLINEXBRL:
|
|
66
|
+
hasInline = True
|
|
67
|
+
if identifiedType in (ModelDocument.Type.INSTANCE, ModelDocument.Type.INLINEXBRL):
|
|
68
|
+
entrypointFiles.append({"file":_path})
|
|
69
|
+
if hasInline: # group into IXDS if plugin feature is available
|
|
70
|
+
for pluginXbrlMethod in PluginManager.pluginClassMethods("InlineDocumentSet.Discovery"):
|
|
71
|
+
pluginXbrlMethod(filesource, entrypointFiles)
|
|
72
|
+
|
|
73
|
+
return entrypointFiles
|