# AUTO-GENERATED by scripts/prepare-ttl.sh — DO NOT EDIT MANUALLY

@prefix cc: <http://creativecommons.org/ns#> .
@prefix dcat: <http://www.w3.org/ns/dcat#> .
@prefix dcterms: <http://purl.org/dc/terms/> .
@prefix eli: <http://data.europa.eu/eli/ontology#> .
@prefix era-ecd-types: <http://data.europa.eu/949/concepts/ecd-types/> .
@prefix era-edp-act: <http://data.europa.eu/949/concepts/edp-activity-types/> .
@prefix era-mf: <http://data.europa.eu/949/concepts/maintenance-functions/> .
@prefix era-organisation-roles: <http://data.europa.eu/949/concepts/organisation-roles/> .
@prefix era-permission-types: <http://data.europa.eu/949/concepts/permission-types/> .
@prefix era-sh: <http://data.europa.eu/949/shapes/> .
@prefix era-sn: <http://data.europa.eu/949/concepts/sol-natures/> .
@prefix era-states-eratv: <http://data.europa.eu/949/concepts/states/eratv/> .
@prefix era-vehicle-types-eratv: <http://data.europa.eu/949/concepts/vehicle-types/eratv/> .
@prefix era: <http://data.europa.eu/949/> .
@prefix ex: <http://example.org/ns#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix geo: <http://www.opengis.net/ont/geosparql#> .
@prefix geosparql: <http://www.opengis.net/ont/geosparql#> .
@prefix goodrel: <http://purl.org/goodrelations/v1#> .
@prefix locn: <http://w3.org/ns/locn#> .
@prefix org: <http://www.w3.org/ns/org#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix prov: <http://www.w3.org/ns/prov#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
@prefix time: <http://www.w3.org/2006/time#> .
@prefix unit: <http://qudt.org/vocab/unit/> .
@prefix vcard: <http://www.w3.org/2006/vcard/ns#> .
@prefix vpa: <https://w3id.org/vpa#> .
@prefix vs: <http://www.w3.org/2003/06/sw-vocab-status/ns#> .
@prefix wgs: <http://www.w3.org/2003/01/geo/wgs84_pos#> .
@prefix xml: <http://www.w3.org/XML/1998/namespace> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .


# ----------------------------------------------------------------------------
# Federated checks for CLD organisation roles
# Target resources are in ERADIS-KG; organisation-role evidence is in OCR-KG.
# ----------------------------------------------------------------------------

era-sh:CLDFederatedOrganisationRoleShape
    a sh:NodeShape ;
    rdfs:label "CLD federated organisation-role validation"@en ;
    rdfs:comment "Collection of federated SHACL-SPARQL constraints for CLD. Constraints are linked from era-sh:CertificationLevelDocumentShape in ED-CLD.ttl."@en ;

    sh:sparql era-sh:DCAudienceOrganisationRoleFederatedSPARQL ;
    sh:sparql era-sh:DCAudienceRoleOfBodyFederatedSPARQL ;
    sh:sparql era-sh:DCAudienceAllowedRoleFederatedSPARQL ;

    sh:sparql era-sh:DCContributorOrganisationRoleFederatedSPARQL ;
    sh:sparql era-sh:DCContributorRoleOfBodyFederatedSPARQL ;
    sh:sparql era-sh:DCContributorAllowedRoleFederatedSPARQL ;

    sh:sparql era-sh:DCCreatorOrganisationRoleFederatedSPARQL ;
    sh:sparql era-sh:DCCreatorRoleOfBodyFederatedSPARQL ;
    sh:sparql era-sh:DCCreatorAllowedRoleFederatedSPARQL ;

    sh:sparql era-sh:DCSubjectNoModuleDecisionFederatedSPARQL ;
    sh:sparql era-sh:DCSubjectAllowedTypesFederatedSPARQL ;
    sh:sparql era-sh:DCSubjectNoICRequiresTSIFederatedSPARQL ;
    sh:sparql era-sh:DCSubjectAtMostOneICFederatedSPARQL ;
    sh:sparql era-sh:DCSourceNotTSIorICFederatedSPARQL ;
    sh:sparql era-sh:VPAComplianceFederatedSPARQL .


era-sh:DCAudienceOrganisationRoleFederatedSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "Organisation using the CLD as Applicant MUST be an era:OrganisationRole, resolved locally or in OCR-KG."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>
        PREFIX era: <http://data.europa.eu/949/>

        SELECT $this ?orgRole WHERE {
          $this dcterms:audience ?orgRole .
          FILTER NOT EXISTS {
            SERVICE <repository:OCR-KG> { ?orgRole a era:OrganisationRole . }
          }
        }
    """ .

era-sh:DCAudienceRoleOfBodyFederatedSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "Exactly one era:Body MUST be linked via era:roleOf for dcterms:audience, resolved locally or in OCR-KG."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>
        PREFIX era: <http://data.europa.eu/949/>

        SELECT $this ?orgRole ?bodyCount WHERE {
          $this dcterms:audience ?orgRole .
          OPTIONAL {
            SELECT ?orgRole (COUNT(DISTINCT ?body) AS ?bodyCount) WHERE {
              SERVICE <repository:OCR-KG> {
                ?orgRole era:roleOf ?body .
                ?body a era:Body .
              }
            }
            GROUP BY ?orgRole
          }
          FILTER(COALESCE(?bodyCount, 0) != 1)
        }
    """ .

era-sh:DCAudienceAllowedRoleFederatedSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "For dcterms:audience, exactly one allowed organisation role (Applicant or Manufacturer) MUST be linked via era:hasOrganisationRole, resolved locally or in OCR-KG."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>
        PREFIX era: <http://data.europa.eu/949/>
        PREFIX era-organisation-roles: <http://data.europa.eu/949/concepts/organisation-roles/>

        SELECT $this ?orgRole ?roleCount WHERE {
          $this dcterms:audience ?orgRole .
          OPTIONAL {
            SELECT ?orgRole (COUNT(DISTINCT ?role) AS ?roleCount) WHERE {
              SERVICE <repository:OCR-KG> {
                ?orgRole era:hasOrganisationRole ?role .
                FILTER(?role IN (era-organisation-roles:Applicant, era-organisation-roles:Manufacturer))
              }
            }
            GROUP BY ?orgRole
          }
          FILTER(COALESCE(?roleCount, 0) != 1)
        }
    """ .


era-sh:DCContributorOrganisationRoleFederatedSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Warning ;
    sh:message "Each dcterms:contributor SHOULD be an era:OrganisationRole, resolved locally or in OCR-KG."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>
        PREFIX era: <http://data.europa.eu/949/>

        SELECT $this ?orgRole WHERE {
          $this dcterms:contributor ?orgRole .
          FILTER isIRI(?orgRole)
          FILTER NOT EXISTS {
            SERVICE <repository:OCR-KG> { ?orgRole a era:OrganisationRole . }
          }
        }
    """ .

era-sh:DCContributorRoleOfBodyFederatedSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Warning ;
    sh:message "For each IRI dcterms:contributor, at least one era:roleOf to an era:Body SHOULD exist, resolved locally or in OCR-KG."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>
        PREFIX era: <http://data.europa.eu/949/>

        SELECT $this ?orgRole WHERE {
          $this dcterms:contributor ?orgRole .
          FILTER isIRI(?orgRole)
          FILTER NOT EXISTS {
            SERVICE <repository:OCR-KG> {
              ?orgRole era:roleOf ?body .
              ?body a era:Body .
            }
          }
        }
    """ .

era-sh:DCContributorAllowedRoleFederatedSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Warning ;
    sh:message "For each IRI dcterms:contributor, at least one allowed organisation role (Manufacturer or AssessmentBody) SHOULD be linked via era:hasOrganisationRole, resolved locally or in OCR-KG."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>
        PREFIX era: <http://data.europa.eu/949/>
        PREFIX era-organisation-roles: <http://data.europa.eu/949/concepts/organisation-roles/>

        SELECT $this ?orgRole WHERE {
          $this dcterms:contributor ?orgRole .
          FILTER isIRI(?orgRole)
          FILTER NOT EXISTS {
            SERVICE <repository:OCR-KG> {
              ?orgRole era:hasOrganisationRole ?role .
              FILTER(?role IN (era-organisation-roles:Manufacturer, era-organisation-roles:AssessmentBody))
            }
          }
        }
    """ .


era-sh:DCCreatorOrganisationRoleFederatedSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "dcterms:creator MUST be an era:OrganisationRole, resolved locally or in OCR-KG."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>
        PREFIX era: <http://data.europa.eu/949/>

        SELECT $this ?orgRole WHERE {
          $this dcterms:creator ?orgRole .
          FILTER NOT EXISTS {
            SERVICE <repository:OCR-KG> { ?orgRole a era:OrganisationRole . }
          }
        }
    """ .

era-sh:DCCreatorRoleOfBodyFederatedSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "Exactly one era:Body MUST be linked via era:roleOf for dcterms:creator, resolved locally or in OCR-KG."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>
        PREFIX era: <http://data.europa.eu/949/>

        SELECT $this ?orgRole ?bodyCount WHERE {
          $this dcterms:creator ?orgRole .
          OPTIONAL {
            SELECT ?orgRole (COUNT(DISTINCT ?body) AS ?bodyCount) WHERE {
              SERVICE <repository:OCR-KG> {
                ?orgRole era:roleOf ?body .
                ?body a era:Body .
              }
            }
            GROUP BY ?orgRole
          }
          FILTER(COALESCE(?bodyCount, 0) != 1)
        }
    """ .

era-sh:DCCreatorAllowedRoleFederatedSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "For dcterms:creator, exactly one allowed organisation role (CAB, AssessmentBody or NoBo) MUST be linked via era:hasOrganisationRole, resolved locally or in OCR-KG."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>
        PREFIX era: <http://data.europa.eu/949/>
        PREFIX era-organisation-roles: <http://data.europa.eu/949/concepts/organisation-roles/>

        SELECT $this ?orgRole ?roleCount WHERE {
          $this dcterms:creator ?orgRole .
          OPTIONAL {
            SELECT ?orgRole (COUNT(DISTINCT ?role) AS ?roleCount) WHERE {
              SERVICE <repository:OCR-KG> {
                ?orgRole era:hasOrganisationRole ?role .
                FILTER(?role IN (era-organisation-roles:CAB, era-organisation-roles:AssessmentBody, era-organisation-roles:NoBo))
              }
            }
            GROUP BY ?orgRole
          }
          FILTER(COALESCE(?roleCount, 0) != 1)
        }
    """ .


era-sh:DCSourceNotTSIorICFederatedSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "dcterms:source must not be an era:TSI or era:IC (classification resolved in era-lex)."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>
        PREFIX era: <http://data.europa.eu/949/>

        SELECT $this ?covered WHERE {
          $this dcterms:source ?covered .
          FILTER EXISTS {
            SERVICE <repository:era-lex> {
              ?covered a ?forbiddenType .
              FILTER(?forbiddenType IN (era:TSI, era:IC))
            }
          }
        }
    """ .


era-sh:DCSubjectAtMostOneICFederatedSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "There must be at most one era:IC as dcterms:subject (type resolved in era-lex)."@en ;
    sh:select """
        PREFIX era: <http://data.europa.eu/949/>
        PREFIX dcterms: <http://purl.org/dc/terms/>

        SELECT $this WHERE {
          $this dcterms:subject ?ic .
          SERVICE <repository:era-lex> {
            ?ic a era:IC .
          }
        }
        GROUP BY $this
        HAVING (COUNT(DISTINCT ?ic) > 1)
    """ .


era-sh:DCSubjectAllowedTypesFederatedSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "Each dcterms:subject MUST be typed as era:IC or era:TSI (type resolved in era-lex)."@en ;
    sh:select """
        PREFIX era: <http://data.europa.eu/949/>
        PREFIX dcterms: <http://purl.org/dc/terms/>

        SELECT $this ?subject WHERE {
          $this dcterms:subject ?subject .
          FILTER NOT EXISTS {
            SERVICE <repository:era-lex> {
              ?subject a ?subjectType .
              FILTER(?subjectType IN (era:IC, era:TSI))
            }
          }
        }
    """ .


era-sh:DCSubjectNoICRequiresTSIFederatedSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "When no era:IC is linked as dcterms:subject, at least one era:TSI MUST be linked (types resolved in era-lex)."@en ;
    sh:select """
        PREFIX era: <http://data.europa.eu/949/>
        PREFIX dcterms: <http://purl.org/dc/terms/>

        SELECT $this WHERE {
          FILTER NOT EXISTS {
            $this dcterms:subject ?ic .
            SERVICE <repository:era-lex> {
              ?ic a era:IC .
            }
          }
          FILTER NOT EXISTS {
            $this dcterms:subject ?tsi .
            SERVICE <repository:era-lex> {
              ?tsi a era:TSI .
            }
          }
        }
    """ .


era-sh:DCSubjectNoModuleDecisionFederatedSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "dcterms:subject MUST NOT be <http://data.europa.eu/eli/dec/2010/713/oj> (Module Decision)."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>

        SELECT $this ?subject WHERE {
          $this dcterms:subject ?subject .
          FILTER(?subject = <http://data.europa.eu/eli/dec/2010/713/oj>)
        }
    """ .


era-sh:VPAComplianceFederatedSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Warning ;
    sh:message "Both `dcterms:subject` and `vpa:checkedCompliance/vpa:checkedRequirement` are used to express compliance to TSI/IC. Prefer only one method."@en ;
    sh:select """
        PREFIX era: <http://data.europa.eu/949/>
        PREFIX vpa: <https://w3id.org/vpa#>
        PREFIX dcterms: <http://purl.org/dc/terms/>

        SELECT $this ?subj ?req ?check WHERE {
          $this dcterms:subject ?subj ;
                vpa:checkedCompliance ?check .
          ?check vpa:checkedRequirement ?req .
          FILTER EXISTS {
            SERVICE <repository:era-lex> {
              ?req a ?type2 .
              FILTER(?type2 IN (era:TSI, era:IC))
            }
          }
        }
    """ .


era-sh:CertificationLevelDocumentShape
    a sh:NodeShape ;
    sh:targetClass era:CertificationLevelDocument  ;
    sh:nodeKind sh:IRI ;
    rdfs:label "Certification Level Documents (CLD) Entry"@en ;
    rdfs:comment """Certification Level Documents (CLD) issued by NoBos are:
    • EC Certificates
    • QMS-Approvals
    and whether these are Intermediate Statements of Verification (ISVs).
    The EU legislative documents 768/2008/EC, (EU) 2016/797, (EU) 2019/250 and 2010/713/EU establish a number of requirements on the content of CLDs."""@en ;
    
        # ----------- Shape document management
    dcterms:creator [ 
      foaf:name "Maarten Duhoux" ;
      dcterms:source <https://orcid.org/0009-0003-6653-6123> ;
      org:memberOf <http://publications.europa.eu/resource/authority/corporate-body/ERA>
    ] ;
    rdfs:seeAlso "https://eradis.era.europa.eu/interop_docs/ecDecl/default.aspx?DocumentType=ECDeclVer"^^xsd:anyURI ;
    rdfs:seeAlso "https://eradis.era.europa.eu/interop_docs/ecDecl/default.aspx?DocumentType=ECDeclCnf"^^xsd:anyURI ;
    rdfs:seeAlso "https://eradis.era.europa.eu/interop_docs/ecDecl/default.aspx?DocumentType=ECDeclSuit"^^xsd:anyURI ;
    owl:versionInfo "3.3.0" ;
    
    dcterms:created "2024-10-25"^^xsd:date ;
    # dcterms:modified ;
    
    # Identifiers
    sh:property era-sh:DCIdentifier ;
    sh:property era-sh:VPAVersionNumber ; 

    # Temporal
    sh:property era-sh:DCCreated ;
    sh:property era-sh:DCIssued ;
    sh:property era-sh:DCReplaces ;
    sh:property era-sh:DCAvailable ;

    # Other properties
    sh:property era-sh:DCType ;
    sh:property era-sh:DCSubject ;
    sh:property era-sh:VPACheckCompliance ;
    sh:property era-sh:DCDescription ; 
    sh:property era-sh:SeeAlso ; 
    sh:property era-sh:DCAudience ;
    sh:property era-sh:DCContributor ;
    sh:property era-sh:DCCreator ;
    sh:property era-sh:DCSource ;
    sh:property era-sh:DCSpatial ;
    sh:property era-sh:Comment ;
    sh:property era-sh:DCIsreplacedBy  ;
    sh:property era-sh:ResultStatement ;
    sh:property era-sh:ValidityStatement ;
    sh:property era-sh:SequenceStatement ;
    sh:property era-sh:ResultAnnex ;
    sh:property era-sh:ResultReport ;

# FIXME: Lifecycle / Provenance (uncomment when EDP-Activity.ttl is ready)
    sh:property era-sh:EDPChangeActivityPropertyShape ;
    sh:property era-sh:EDPStatePropertyShape ;

    # SPARQL Constraints
    sh:sparql era-sh:ConformsToSPARQL ;
    sh:sparql era-sh:CLDSKOS ; 
    sh:sparql era-sh:VersionSPARQL  ; 
    sh:sparql era-sh:ConformsToSKOS ;
    sh:sparql era-sh:DCIsreplacedByVersionNumberSPARQL ;
    sh:sparql era-sh:DCIsreplacedByDCIssuedSPARQL ;
    sh:sparql era-sh:VPAComplianceFederatedSPARQL ;

    # Federated SPARQL Constraints (defined in ED-CLD.federated.ttl)
    sh:sparql era-sh:DCAudienceOrganisationRoleFederatedSPARQL ;
    sh:sparql era-sh:DCAudienceRoleOfBodyFederatedSPARQL ;
    sh:sparql era-sh:DCAudienceAllowedRoleFederatedSPARQL ;
    sh:sparql era-sh:DCContributorOrganisationRoleFederatedSPARQL ;
    sh:sparql era-sh:DCContributorRoleOfBodyFederatedSPARQL ;
    sh:sparql era-sh:DCContributorAllowedRoleFederatedSPARQL ;
    sh:sparql era-sh:DCCreatorOrganisationRoleFederatedSPARQL ;
    sh:sparql era-sh:DCCreatorRoleOfBodyFederatedSPARQL ;
    sh:sparql era-sh:DCCreatorAllowedRoleFederatedSPARQL ;
    sh:sparql era-sh:DCSubjectNoModuleDecisionFederatedSPARQL ;
    sh:sparql era-sh:DCSubjectAllowedTypesFederatedSPARQL ;
    sh:sparql era-sh:DCSubjectNoICRequiresTSIFederatedSPARQL ;
    sh:sparql era-sh:DCSubjectAtMostOneICFederatedSPARQL ;
    sh:sparql era-sh:DCSourceNotTSIorICFederatedSPARQL .

# --- CLD properties (updated for real data) ---
era-sh:DCIdentifier
    a sh:PropertyShape ;
    sh:path dcterms:identifier ;
    era:affectedProperty dcterms:identifier ;
    sh:name "CLD identifier"@en ;
    sh:datatype xsd:string ;
    # FIXME sh:pattern "{HERE}" ;
    sh:nodeKind sh:Literal ;
    sh:description "The CLD ID, as defined in RFU-STR-001."@en;
    sh:message "The CLD ID, as defined in RFU-STR-001, MUST be present."@en;
    sh:severity sh:Violation ;
    sh:minCount 1;
    sh:maxCount 1 .

era-sh:VPAVersionNumber  
    a sh:PropertyShape ;
    sh:path vpa:versionNumber ;
    era:affectedProperty vpa:versionNumber ;
    sh:name "CLD Version Number"@en ;
    sh:datatype xsd:integer ;
    sh:nodeKind sh:Literal ;
    sh:description "The Version Counter of the CLD, as defined in RFU-STR-001."@en;
    sh:message "The Version Counter of the CLD, as defined in RFU-STR-001, MUST be present."@en;
    sh:severity sh:Violation ;
    sh:minCount 1;
    sh:maxCount 1 .

era-sh:DCCreated   
    a sh:PropertyShape ;
    sh:path dcterms:created ;
    era:affectedProperty dcterms:created ;
    sh:name "CLD entry created in KG"@en ;
    sh:datatype xsd:date ;
    sh:nodeKind sh:Literal ;
    sh:description "Date the CLD was created in ERADIS+."@en ;
    sh:message "Date the CLD was created in ERADIS+ MUST be present."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 .

era-sh:DCIssued  
    a sh:PropertyShape ;
    sh:path dcterms:issued ;
    era:affectedProperty dcterms:issued ;
    sh:name "CLD Issue date"@en ;
    sh:datatype xsd:date ;
    sh:nodeKind sh:Literal ;
    sh:description "Date the CLD was issued by the CAB."@en ;
    sh:message "Date the CLD was issued by the CAB, MUST be present."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 .

era-sh:DCAvailable
    a sh:PropertyShape ;
    sh:path dcterms:available ;
    era:affectedProperty dcterms:available ;
    sh:name "ERADIS Availability Date" ;
    sh:datatype xsd:date ;
    sh:nodeKind sh:Literal ;
    sh:description "Date when this CLD became available in the ERADIS public database." ;
    sh:message "ERADIS availability date SHOULD be present as xsd:date."@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 ;
    sh:maxCount 1 .

era-sh:DCReplaces  
    a sh:PropertyShape ;
    sh:path dcterms:replaces ;
    era:affectedProperty dcterms:replaces ;
    sh:name "Which CLD this CLD replaces"@en ;
    sh:nodeKind sh:IRI ;
    sh:description "The previous CLD replaced by this one, if any."@en ;
    sh:message "(Check) This CLD replaces no other CLD."@en ;
    sh:severity sh:Warning ;
    sh:maxCount 1 .

era-sh:DCType 
    a sh:PropertyShape ;
    sh:path dcterms:type ;
    era:affectedProperty dcterms:type ;
    sh:name "CLD type"@en ;
    sh:nodeKind sh:IRI ;
    sh:description "SKOS Concept indicating what type of CLD is represented."@en ;
    sh:message "Type of CLD MUST be present."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 .

era-sh:ConformsToSPARQL   
    a sh:SPARQLConstraint ;
    sh:severity sh:Warning ;
    sh:message "No `era:Document` (representing ERA Technical Document 000MRA1044) is present in `dcterms:conformsTo`."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>
        PREFIX era: <http://data.europa.eu/949/>

        SELECT $this WHERE {
          FILTER NOT EXISTS {
            $this dcterms:conformsTo ?doc .
            ?doc a era:Document .
          }
        }
    """ .

era-sh:CLDSKOS 
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:description "CLD Module (SKOS Concept)."@en ;
    sh:message "Module MUST be included as exactly ONE SKOS Concept (module)."@en ;
    sh:select """
        PREFIX era: <http://data.europa.eu/949/>
        PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
        PREFIX dcterms: <http://purl.org/dc/terms/>

        SELECT $this WHERE {
          $this dcterms:conformsTo ?doc .
          ?doc a skos:Concept .
        }
        GROUP BY $this
        HAVING (COUNT(?doc) != 1)
    """ .

era-sh:VersionSPARQL  
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "The `era:Document` in `dcterms:conformsTo` MUST not have a version 1.1 or 2017 as it was issued on {?issued}, which is after Decision ERA-ED-DEC 2110-2022 of 13/12/2022."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>
        PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
        PREFIX era: <http://data.europa.eu/949/>
        PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>

        SELECT $this ?doc ?comment ?issued WHERE {
          $this dcterms:conformsTo ?doc ; dcterms:issued ?issued .
          ?doc a era:Document ;
               rdfs:comment ?comment .
          FILTER(
            (CONTAINS(LCASE(STR(?comment)), "1.1") || CONTAINS(LCASE(STR(?comment)), "2017")) &&
            (?issued > "2022-12-13"^^xsd:date)
          )
        }
    """ .

era-sh:ConformsToSKOS   
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "No SKOS Concept (for the module) is present in dcterms:conformsTo."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>
        PREFIX skos: <http://www.w3.org/2004/02/skos/core#>

        SELECT $this WHERE {
        FILTER NOT EXISTS {
            $this dcterms:conformsTo ?mod .
            ?mod a skos:Concept .
        }
        }
    """ .

era-sh:DCSubject 
    a sh:PropertyShape ;
    sh:path dcterms:subject ;
    era:affectedProperty dcterms:subject ;
    sh:name "CLD Subject of Assessment"@en ;
    sh:nodeKind sh:IRI ;
    sh:description "Object(s) of assessment, validated with federated checks from era-lex: not the Module Decision URI, at most one era:IC, and valid era:IC/era:TSI typing."@en ;
    sh:message "dcterms:subject MUST follow CLD rules: no Module Decision URI, at most one era:IC, and valid era:IC/era:TSI typing (with at least one era:TSI when no era:IC is present)."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 .

era-sh:VPACheckCompliance    
    a sh:PropertyShape ;
    sh:path vpa:checkedCompliance ;
    era:affectedProperty vpa:checkedCompliance ;
    sh:name "CLD Compliances Checked"@en ;
    sh:nodeKind sh:BlankNodeOrIRI ;
    sh:or (
        [ sh:class era:CABAssessmentCheck ]
        [ sh:class era:CABAuditCheck ]
    ) ;
    sh:description "Any compliance or audit checks (CABAssessmentCheck or CABAuditCheck) performed for this CLD."@en ;
    sh:message "CLD contains no compliance or audit checks (CABAssessmentCheck or CABAuditCheck)."@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 .

era-sh:DCDescription    
    a sh:PropertyShape ;
    sh:path dcterms:description ;
    era:affectedProperty dcterms:description ;
    sh:name "CLD description"@en ;
    sh:or ( [sh:datatype xsd:string ] [sh:datatype rdf:langString ] ) ;
    sh:nodeKind sh:Literal ;
    sh:description "Human-readable description of the CLD."@en ;
    sh:message "Human-readable Object of Assessment of the CLD MUST be present."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ; sh:maxCount 1 .

era-sh:SeeAlso    
    a sh:PropertyShape ;
    sh:path rdfs:seeAlso ;
    era:affectedProperty rdfs:seeAlso ;
    sh:name "CLD link to ERADIS URL"@en ;
    sh:datatype xsd:anyURI ;
    sh:nodeKind sh:Literal ;
    sh:description "The ERADIS or other URL of the CLD, if available."@en ;
    sh:message "(Check) No ERADIS or other URL of the CLD available?"@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 ; sh:maxCount 1 .
  
era-sh:DCAudience    
    a sh:PropertyShape ;
    sh:path dcterms:audience ;
    era:affectedProperty dcterms:audience ;
    sh:name "Organisation receiving the CLD"@en ;
    sh:nodeKind sh:IRI ;
    sh:description "The organisation using the CLD as Applicant. Cardinality and node kind are validated locally; role semantics are validated in the federated CLD shapes."@en ;
    sh:message "Organisation receiving the CLD as Applicant MUST be provided as one IRI in ERADIS-KG."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ; sh:maxCount 1 .

era-sh:DCContributor   
    a sh:PropertyShape ;
    sh:path dcterms:contributor ;
    era:affectedProperty dcterms:contributor ;
    sh:name "Involved organisations"@en ;
    sh:nodeKind sh:BlankNodeOrIRI ;
    sh:description "Organisations involved in the assessment. Presence and node kind are validated locally; role semantics are validated in the federated CLD shapes."@en ;
    sh:severity sh:Warning ;
    sh:message "At least one organisation SHOULD be linked as dcterms:contributor."@en ;
    sh:minCount 1 .

era-sh:DCCreator    
    a sh:PropertyShape ;
    sh:path dcterms:creator ;
    era:affectedProperty dcterms:creator ;
    sh:name "The CAB producing the CLD"@en ;
    sh:nodeKind sh:IRI ;
    sh:description "CAB issuing the CLD. Cardinality and node kind are validated locally; role semantics are validated in the federated CLD shapes."@en ;
    sh:severity sh:Violation ;
    sh:message "Exactly one issuing organisation MUST be linked as dcterms:creator."@en ;
    sh:minCount 1 ; sh:maxCount 1 .

era-sh:DCSource  
    a sh:PropertyShape ;
    sh:path dcterms:source ;
    era:affectedProperty dcterms:source ;
    sh:name "CLD: Covered Directives"@en ;
    sh:nodeKind sh:IRI ;
    sh:class vpa:Requirement ;
    sh:description "Legal Requirement(s), but not TSI's, covered by the CLD. Must be a `vpa:Requirement`, and NOT an `era:TSI` or `era:IC`."@en ;
    sh:message "The property `dcterms:source` MUST be present and link to a `vpa:Requirement` that is not an `era:TSI` nor an `era:IC`."@en ;
    sh:minCount 1 .

era-sh:DCSpatial    
    a sh:PropertyShape ;
    sh:path dcterms:spatial ;
    era:affectedProperty dcterms:spatial ;
    sh:name "CLD: spatial validity to a Site"@en ;
    sh:nodeKind sh:BlankNodeOrIRI ;
    sh:class org:Site ;
    sh:description "Manufacturing or assessment site(s), if relevant."@en ;
    sh:message "No Manufacturing or assessment site(s) mentioned in the CLD."@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 .

era-sh:Comment   
    a sh:PropertyShape ;
    sh:path rdfs:comment ;
    era:affectedProperty rdfs:comment ;
    sh:name "CLD comment"@en ;
    sh:or ( [sh:datatype xsd:string ] [sh:datatype rdf:langString ] ) ;
    sh:nodeKind sh:Literal ;
    sh:description "Free-text comment about the CLD."@en ;
    sh:message "No Free-text comment about the CLD is present"@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 ; sh:maxCount 1 .

era-sh:DCIsreplacedBy    
    a sh:PropertyShape ;
    sh:path dcterms:isReplacedBy ;
    era:affectedProperty dcterms:isReplacedBy ;
    sh:name "Replacing CLD"@en ;
    sh:nodeKind sh:IRI ;
    sh:description "IRI of the newer CLD that replaces this one, if any."@en ;
    sh:message "(Check) This CLD is not linking to a newer CLD."@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 ; sh:maxCount 1 .

era-sh:DCIsreplacedByVersionNumberSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "If `dcterms:isReplacedBy` is present, the replacing CLD MUST have a higher `vpa:versionNumber` than this CLD."@en ;
    sh:select """
        PREFIX vpa: <https://w3id.org/vpa#>
        PREFIX dcterms: <http://purl.org/dc/terms/>

        SELECT $this ?replacer ?thisVersion ?replacerVersion WHERE {
          OPTIONAL { $this vpa:versionNumber ?thisVersion . }
          OPTIONAL { 
            $this dcterms:isReplacedBy ?replacer .
            ?replacer vpa:versionNumber ?replacerVersion . 
          }
          FILTER(bound(?replacer) && bound(?thisVersion) && bound(?replacerVersion) && (?replacerVersion <= ?thisVersion))
        }
    """ .

era-sh:DCIsreplacedByDCIssuedSPARQL   
    a sh:SPARQLConstraint ;
    sh:message "If `dcterms:isReplacedBy` is present, the replacing CLD MUST have a later `dcterms:issued` date than this CLD."@en ;
    sh:severity sh:Violation ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>

        SELECT $this ?replacer ?thisIssued ?replacerIssued WHERE {
            OPTIONAL { $this dcterms:issued ?thisIssued . }
            OPTIONAL { 
              $this dcterms:isReplacedBy ?replacer .
              ?replacer dcterms:issued ?replacerIssued . }
            FILTER(bound(?replacer) && bound(?thisIssued) && bound(?replacerIssued) && (?replacerIssued <= ?thisIssued))
        }
    """ .

era-sh:ResultStatement
    a sh:PropertyShape ;
    sh:path era:resultStatement ;
    era:affectedProperty era:resultStatement ;
    sh:name "CLD Assessment Result"@en ;
    sh:datatype rdf:langString ;
    sh:nodeKind sh:Literal ;
    sh:description "Assessment result (free text, required)."@en ;
    sh:message "Assessment result is required."@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 ; sh:maxCount 1 .

era-sh:ValidityStatement
    a sh:PropertyShape ;
    sh:path era:validityStatement ;
    era:affectedProperty era:validityStatement ;
    sh:name "CLD Validity Statement"@en ;
    sh:datatype rdf:langString ;
    sh:nodeKind sh:Literal ;
    sh:description "Validity statement (free text, required)."@en ;
    sh:message "Validity statement is required."@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 ; sh:maxCount 1 .

era-sh:SequenceStatement
    a sh:PropertyShape ;
    sh:path era:sequenceStatement ;
    era:affectedProperty era:sequenceStatement ;
    sh:name "CLD Sequence Statement"@en ;
    sh:datatype rdf:langString ;
    sh:nodeKind sh:Literal ;
    sh:description "Sequence statement (free text, optional)."@en ;
    sh:message "Sequence statement is not present."@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 ; sh:maxCount 1 .


# --- CABCheck shape (for vpa:checkedCompliance, covers both assessment and audit checks) ---
era-sh:CABCheckShape 
    a sh:NodeShape ;
    rdfs:label "CAB Assessment/Audit Check"@en ;
    rdfs:comment "Validates conformity assessment or audit checks performed by Conformity Assessment Bodies (CABs) within CLD documentation."@en ;
    sh:targetClass era:CABAssessmentCheck,
                   era:CABAuditCheck ;
    sh:nodeKind sh:IRI ;
    sh:property era-sh:CABCheckCheckedSection ;
    sh:property era-sh:CABCheckComplianceResult ;
    sh:property era-sh:CABCheckIsOptional ;
    sh:property era-sh:CABCheckCheckedRequirement ;
    sh:property era-sh:CABCheckWithRestriction ;
    sh:property era-sh:CABCheckComment ;
    sh:property era-sh:CABCheckSeeAlso .


era-sh:CABCheckCheckedSection
    a sh:PropertyShape ;
    sh:path vpa:checkedSection ;
    era:affectedProperty vpa:checkedSection ;
    sh:name "Sections checked"@en ;
    sh:nodeKind sh:IRI ;
    sh:description "Section of the legal text checked in this check."@en ;
    sh:message "The CAB's Check does not refer to a section of legal text."@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 .


era-sh:CABCheckComplianceResult
    a sh:PropertyShape ;
    sh:path [ sh:alternativePath ( vpa:isCompliant era:isCompliant ) ] ;
    era:affectedProperty era:isCompliant ;
    era:affectedClass era:Compliance ;
    sh:name "Compliance Check Result"@en ;
    sh:or (
        [
            sh:datatype xsd:boolean ;
            sh:nodeKind sh:Literal ;
            sh:description "Whether the section is compliant (true/false) using vpa:isCompliant."@en
        ]
        [
            sh:nodeKind sh:IRI ;
            sh:class skos:Concept ;
            sh:description "IRI to a skos:Concept indicating compliance (Y/N/P) using era:isCompliant."@en
        ]
    ) ;
    sh:message "The CAB's Check has not expressed compliance using either `vpa:isCompliant` or `era:isCompliant`."@en ;
    sh:severity sh:Warning ;
    sh:maxCount 1 ;
    sh:minCount 1 .

era-sh:CABCheckIsOptional
    a sh:PropertyShape ;
    sh:path era:isOptional ;
    era:affectedClass era:Compliance ;
    era:affectedProperty era:isOptional ;
    sh:name "Optional Compliance Check"@en ;
    sh:datatype xsd:boolean ;
    sh:nodeKind sh:Literal ;
    sh:description "Indicates whether the CAB check concerns an optional function or optional requirement."@en ;
    sh:message "The CAB's Check optional flag must be expressed as a boolean value."@en ;
    sh:severity sh:Warning ;
    sh:maxCount 1 ;
    sh:minCount 0 .

era-sh:CABCheckCheckedRequirement
    a sh:PropertyShape ;
    sh:path vpa:checkedRequirement ;
    era:affectedProperty vpa:checkedRequirement ;
    sh:name "The Legal source of the check"@en ;
    sh:nodeKind sh:IRI ;
    sh:description "Legal requirement(s) checked in this check."@en ;
    sh:message "The CAB's Check has not cited the Legal reference used (TSI, or other LegalWork)."@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 .

era-sh:CABCheckWithRestriction
    a sh:PropertyShape ;
    sh:path vpa:withRestriction ;
    era:affectedProperty vpa:withRestriction ;
    sh:name "Restriction formulated during Check"@en ;
    sh:nodeKind sh:BlankNodeOrIRI ;
    sh:class era:CABRestriction ;
    sh:description "Restriction, addition, or condition (CLOU) attached to this check."@en ;
    sh:message "The CAB's Check has not expressed any Restriction."@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 .

era-sh:CABCheckComment
    a sh:PropertyShape ;
    sh:path rdfs:comment ;
    era:affectedProperty rdfs:comment ;
    sh:name "Check comment"@en ;
    sh:or ( [sh:datatype xsd:string ] [sh:datatype rdf:langString ] ) ;
    sh:nodeKind sh:Literal ;
    sh:description "Free-text comment about the check."@en ;
    sh:message "The CAB's Check was not commented."@en ;
    sh:severity sh:Warning ;
    sh:maxCount 1 ;
    sh:minCount 1 .

era-sh:CABCheckSeeAlso
    a sh:PropertyShape ;
    sh:path rdfs:seeAlso ;
    era:affectedProperty rdfs:seeAlso ;
    sh:name "see Also"@en ;
    sh:datatype xsd:anyURI ;
    sh:nodeKind sh:Literal ;
    sh:description "Reference to supporting documents or reports."@en ;
    sh:message "The CAB's Check has not refered to a further documented reference URL."@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 .

# Section 2 of a vehicle type should be resolved through Restriction and Compliance resources.
# Conformity of a VehicleType to TSI's
era-sh:TSIConformity  
    a sh:NodeShape  ;
    rdfs:label "TSI Conformity Shape"@en ;
    rdfs:comment "Validates TSI conformity declarations for vehicle types, ensuring proper documentation of compliance checks and restrictions."@en ;
    dcterms:created "2024-10-25"^^xsd:date ;
    ## Properties -----------
    sh:property era-sh:TSIConformityRegarding .

era-sh:TSIConformityRegarding
    a sh:PropertyShape ;
    sh:path vpa:regarding ;
    era:affectedProperty vpa:regarding ;
    sh:name "TSI Check restriction related to TSI"@en ;
    sh:nodeKind sh:IRI ;
    sh:class era:Requirement ; # Use an exact link to the vehicle-related TSI examined /ERALEX
    sh:description "The TSI for which conformity was checked in this VehicleType. Selection from a predefined list of vehicle related TSIs (both in force and those that were previously in force) (multiple selection possible)"@en ;
    sh:message "The TSI conformity did not express what TSI was the source."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 .

era-sh:TSIComplianceCheck   
    a sh:NodeShape ;
    rdfs:label "Vehicle Type Certificate: Conformity with TSI - compliance check"@en ;
    rdfs:comment "This shape validates that a vehicle type includes proper compliance checks (section 2.2 or 2.3 of the certificate) ensuring conformity with applicable TSIs." ;
    dcterms:created "2024-10-25"^^xsd:date ;
    rdfs:seeAlso "https://eur-lex.europa.eu/eli/dec_impl/2011/665/2023-09-08#anx_II" ;
    dcterms:source <http://data.europa.eu/eli/dec_impl/2011/665/2023-09-08> ;
    # FIXME: targetClass is missing and exact use is not certain through Certificate,
   # It may not be era:VehicleType but the compliance check IN there.

    sh:property era-sh:TSIComplianceCheckChecked ;
    sh:property era-sh:TSIComplianceCheckResult ;
    sh:property era-sh:TSIComplianceCheckCheckedSection ;
    sh:property era-sh:TSIComplianceCheckForConfiguration .


    ## properties
era-sh:TSIComplianceCheckChecked
    a sh:PropertyShape ;
    sh:path [ sh:inversePath vpa:checked ] ;
    era:affectedProperty vpa:checked ;
    sh:name "checked"@en ;
    sh:class era:CertificationLevelDocument ;
    era:eratvIndex "2.2" ;
    sh:description  "The certificate, to be mentioned under 2.2 of the VehicleType, which examined Sections and Specific Cases, and their conclusion."@en ;
    # "EC certificate of verification : Reference of 'EC type examination certificates' (if module SB applied) and/or 'EC design examination certificates' (if module SH1 applied)"@en
    sh:message "The certificate, to be mentioned under 2.2 of the VehicleType, which examined Sections and Specific Cases, and their conclusion. EC certificate of verification : Reference of 'EC type examination certificates' (if module SB applied) and/or 'EC design examination certificates' (if module SH1 applied)"@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 .


era-sh:TSIComplianceCheckResult
    a sh:PropertyShape ;
    sh:path era:isCompliant ; ## see vpa:isCompliant (allows only boolean)
    era:affectedProperty era:isCompliant ;
    sh:name "TSI Compliance Check Result"@en ;
    sh:nodeKind sh:IRI ;
    era:inSkosConceptScheme <http://data.europa.eu/949/concepts/tsi-conformities/ConformityResults/> ;# to be created in ERA Vocabulary
    sh:class <http://www.w3.org/2004/02/skos/core#Concept> ;
    sh:description "Y: Compliant, N: Non-compliant, P: Partially compliant."@en ;
    sh:message "The type's TSI Compliance Check has not expressed compliance using `era:isCompliant`."@en ;
    sh:severity sh:Warning ;
    sh:maxCount 1 ;
    sh:minCount 1 .

era-sh:TSIComplianceCheckCheckedSection
    a sh:PropertyShape ;
    sh:path vpa:checkedSection ;
    era:affectedProperty vpa:checkedSection ;
    sh:name "TSI Compliance Check (Section checked)"@en ;
    sh:nodeKind sh:IRI ;
    sh:maxCount 1 ;
    sh:minCount 1 ;
    sh:or (
        [
            era:inSkosConceptScheme <http://data.europa.eu/949/concepts/tsi-conformities/TSISpecificCases/> ;# to be created in ERA Vocabulary
            sh:class <http://www.w3.org/2004/02/skos/core#Concept> ;
            sh:description "Applicable specific cases (specific cases conformity with which has been assessed)."@en ;
            sh:message "The type's TSI Compliance Check does not contain `Applicable specific cases`."@en ;
            sh:severity sh:Warning ;
            era:eratvIndex "2.3"
        ]
        [
            era:inSkosConceptScheme <http://data.europa.eu/949/concepts/tsi-conformities/TSISections> ;# to be created in ERA Vocabulary
            sh:class <http://www.w3.org/2004/02/skos/core#Concept> ;
            sh:description "Sections of TSI not complied with."@en ;
            sh:message "The type's TSI Compliance Check does not contain `Sections of TSI not complied with`."@en ;
            sh:severity sh:Warning ;
            era:eratvIndex "2.1"
            # Instances of this TSIComplianceCheck must have vpa:isCompliant "false"^^xsd:boolean
        ]
    ) .

## TODO: In some cases, the ComplianceCheck MUST be linked to a Vehicle Configuration
era-sh:TSIComplianceCheckForConfiguration
        a sh:PropertyShape ;
        sh:path era:forConfiguration ;
        era:affectedProperty era:forConfiguration ;
        sh:name "Compliance Check TSI valid for Configuration"@en ;
        sh:nodeKind sh:IRI ;
        sh:class era:VehicleTypeConfiguration ;
        sh:description "Links the compliance check to the specific vehicle configuration it applies to."@en ;
        sh:minCount 1 ;
        sh:severity sh:Violation ;
        sh:message "The type's TSI Compliance Check does not contain a link to the Configuration it is applicable for."@en  .

era-sh:CLDValidityShape 
    a sh:NodeShape ;
    rdfs:label "CLD Validity Shape"@en ;
    rdfs:comment "Validates that CLD has a validity property with a valid time interval using either start/end dates or start/duration."@en ;
    sh:targetClass era:CertificationLevelDocument ;
    sh:nodeKind sh:IRI ;
    sh:or (
        [ sh:property [
            sh:path vpa:valid ;
            era:affectedProperty vpa:valid ;
            sh:name "Validity Interval (Begin/End)" ;
            sh:description "The validity interval for the CLD (should have a valid beginning and end date)." ;
            sh:qualifiedValueShape era-sh:StartEndValidityShape ;
            sh:qualifiedMinCount 1 ; 
            sh:qualifiedMaxCount 1 ;
            sh:maxCount 1 ;
          ]
        ]
        [ sh:property [
            sh:path vpa:valid ;
            era:affectedProperty vpa:valid ;
            sh:name "Validity Interval (Begin/Duration)" ;
            sh:description "The validity interval for the CLD (should have a valid beginning and duration in years)." ;
            sh:qualifiedValueShape era-sh:StartDurationShape ;
            sh:qualifiedMinCount 1 ; 
            sh:qualifiedMaxCount 1 ;
            sh:maxCount 1 ;
          ]
        ]
    ) ;
    sh:message "CLD must have a validity property with a valid time interval."@en .


era-sh:ResultAnnex
    a sh:PropertyShape ;
    sh:path era:resultAnnex ;
    era:affectedProperty era:resultAnnex ;
    era:affectedClass era:CertificationLevelDocument ;
    sh:name "Assessment/audit result annex"@en ;
    sh:nodeKind sh:Literal ;
    sh:datatype xsd:string ;
    sh:minCount 0 ;
    sh:maxCount 1 ;
    sh:description "Annex of the assessment/audit result."@en ;
    sh:message "resultAnnex: The value must be a string."@en ;
    sh:severity sh:Violation .

era-sh:ResultReport
    a sh:PropertyShape ;
    sh:path era:resultReport ;
    era:affectedProperty era:resultReport ;
    era:affectedClass era:CertificationLevelDocument ;
    sh:name "Assessment/audit result report/file"@en ;
    sh:nodeKind sh:Literal ;
    sh:datatype xsd:string ;
    sh:minCount 0 ;
    sh:maxCount 1 ;
    sh:description "Report about the assessment/audit result, such as a NoBo File or NoBo Conformity Assessment Report, with identifier, revision and date."@en ;
    sh:message "resultReport: The value must be a string."@en ;
    sh:severity sh:Violation .

# ============================================================================
# ERADIS Declaration of Conformity to an Authorised Vehicle Type Shapes
# (SHACL 1.1 compatible)
# ============================================================================
# This file defines SHACL shapes for validating Declarations of conformity
# to an authorised vehicle type:
# - Directive (EU) 2016/797 (Interoperability Directive)
# - Regulation (EU) 402/2013 (Common Safety Method on Risk Evaluation)
# - Decision 768/2008/EC (Common framework for marketing of products)
#
# This Declaration is the declaration established for a vehicle by the applicant
# in which the applicant declares on its sole responsibility that the vehicle
# concerned, which has been subject to the relevant verification procedures,
# conforms to an authorised vehicle type and satisfies the requirements of the
# relevant Union law and relevant national rules.
#
# Note: This is a SHACL 1.1 version. The SHACL 1.2 version with
# sh:targetWhere is in the SHACL 1.2/ subfolder.
# In SHACL 1.1, we use sh:SPARQLTarget (SHACL-AF) to target only
# era:ECDeclaration instances with dcterms:type era-ecd-types:ECDoC2T.
# ============================================================================


# ----------------------------------------------------------------------------
# Main DC2T Shape
# ----------------------------------------------------------------------------
# Uses sh:SPARQLTarget (SHACL-AF) to target only era:ECDeclaration instances
# that have dcterms:type era-ecd-types:ECDoC2T.

era-sh:DC2TeclarationShape 
    a sh:NodeShape ;
    sh:target [
        a sh:SPARQLTarget ;
        sh:select """
            SELECT ?this WHERE {
                ?this a <http://data.europa.eu/949/ECDeclaration> ;
                      <http://purl.org/dc/terms/type> <http://data.europa.eu/949/concepts/ecd-types/ECDoC2T> .
            }
        """
    ] ;

    rdfs:label "Declaration of Conformity to an Authorised Vehicle Type" ;
    rdfs:comment "Validates Declaration of Conformity to an Authorised Vehicle Type according to EU Regulation 2016/797 and Decision 2018/545."@en ;

    # ----------- Shape document management
    dcterms:creator [ 
      foaf:name "Maarten Duhoux" ;
      dcterms:source <https://orcid.org/0009-0003-6653-6123> ;
      org:memberOf <http://publications.europa.eu/resource/authority/corporate-body/ERA>
    ] ;
    skos:editorialNote "These declarations are not published in ERADIS, but exist in the context of `era:VehicleAuthorisationApplication`"@en ;
    owl:versionInfo "3.3.0" ;

    dcterms:created "2026-02-06"^^xsd:date ;
    # dcterms:modified ;
    
    # Core identification properties
    sh:property era-sh:DC2TIdentifier ;
    sh:property era-sh:DC2TType ;
    sh:property era-sh:DC2TVersionNumber ;
    
    # Temporal properties
    sh:property era-sh:DC2TCreated ;
    sh:property era-sh:DC2TIssued ;
    sh:property era-sh:DC2TAvailable ;
    
    # Organizational relationships
    sh:property era-sh:DC2TCreator ;
    sh:property era-sh:DC2TContributor ;
    
    # Technical scope
    sh:property era-sh:DC2TCoverage ;
    sh:property era-sh:DC2TSubject ;
    
    # Descriptive metadata
    sh:property era-sh:DC2TLabel ;
    sh:property era-sh:DC2TComment ;
    sh:property era-sh:DC2TSeeAlso ;
    
    # Versioning and lifecycle
    sh:property era-sh:DC2TReplaces ;
    sh:property era-sh:DC2TStatus ;
        
    # SPARQL constraints
    sh:sparql era-sh:DC2TReplacementVersionSPARQL ;
    sh:sparql era-sh:DC2TReplacementDateSPARQL .

# ----------------------------------------------------------------------------
# DC2T Type constraint
# ----------------------------------------------------------------------------

era-sh:DC2TType
    a sh:PropertyShape ;
    sh:path dcterms:type  ;
    era:affectedProperty dcterms:type  ;
    sh:name "Declaration Type" ;
    sh:nodeKind sh:IRI ;
    sh:hasValue era-ecd-types:ECDoC2T ;
    sh:description "Type of Declaration: must be ECDoC2T (Declaration of Conformity to Type)." ;
    sh:message "Declaration type MUST be ECDoC2T (Declaration of Conformity to an Authorised Vehicle Type)."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 .

# ----------------------------------------------------------------------------
# 1. Core Identification Properties
# ----------------------------------------------------------------------------

era-sh:DC2TIdentifier
    a sh:PropertyShape ;
    sh:path dcterms:identifier  ;
    era:affectedProperty dcterms:identifier  ;
    sh:name "Declaration Identifier" ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:description "Unique identifier for the Declaration." ;
    sh:message "Declaration identifier SHOULD be present."@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 ;
    sh:maxCount 1 .

era-sh:DC2TVersionNumber
    a sh:PropertyShape ;
    sh:path vpa:versionNumber  ;
    era:affectedProperty vpa:versionNumber  ;
    sh:name "Version Number" ;
    sh:datatype xsd:integer ;
    sh:nodeKind sh:Literal ;
    sh:description "Sequential version number of this Declaration (starts at 1, increments with each revision)." ;
    sh:minInclusive 1 ;
    sh:message "Version number MUST be a positive integer starting from 1."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 .

# ----------------------------------------------------------------------------
# 2. Temporal Properties
# ----------------------------------------------------------------------------

era-sh:DC2TCreated
    a sh:PropertyShape ;
    sh:path dcterms:created  ;
    era:affectedProperty dcterms:created  ;
    sh:name "Creation Date" ;
    sh:datatype xsd:date ;
    sh:nodeKind sh:Literal ;
    sh:description "Date when this Declaration resource was created in the knowledge graph." ;
    sh:message "Knowledge graph creation date MUST be present as xsd:date."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 .

era-sh:DC2TIssued
    a sh:PropertyShape ;
    sh:path dcterms:issued  ;
    era:affectedProperty dcterms:issued  ;
    sh:name "Document Issue Date" ;
    sh:datatype xsd:date ;
    sh:nodeKind sh:Literal ;
    sh:description "Official date when the applicant issued this Declaration document." ;
    sh:message "Document issue date MUST be present as xsd:date."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 .

era-sh:DC2TAvailable
    a sh:PropertyShape ;
    sh:path dcterms:available  ;
    era:affectedProperty dcterms:available  ;
    sh:name "Availability Date" ;
    sh:datatype xsd:date ;
    sh:nodeKind sh:Literal ;
    sh:description "Date when this Declaration became available in the public database." ;
    sh:message "Availability date SHOULD be present as xsd:date."@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 ;
    sh:maxCount 1 .

# ----------------------------------------------------------------------------
# 3. Organizational Relationships
# ----------------------------------------------------------------------------

era-sh:DC2TCreator
    a sh:PropertyShape ;
    sh:path dcterms:creator  ;
    era:affectedProperty dcterms:creator  ;
    sh:name "Applicant (Declaration Issuer)" ;
    sh:class era:OrganisationRole ;
    sh:nodeKind sh:BlankNodeOrIRI ;
    sh:description "The applicant issuing this Declaration (must be an era:OrganisationRole with role 'Applicant')." ;
    sh:message "Declaration MUST have exactly one applicant as creator (era:OrganisationRole with role Applicant)."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:node [
        sh:property era-sh:DC2TCreatorRoleOf ;
        sh:property era-sh:DC2TCreatorOrganisationRole
    ] .


era-sh:DC2TCreatorRoleOf
    a sh:PropertyShape ;
    sh:path era:roleOf  ;
    era:affectedProperty era:roleOf  ;
    sh:name "Applicant Body IRI" ;
    sh:nodeKind sh:IRI ;
    sh:class era:Body ;
    sh:description "The legal entity (era:Body) acting as applicant." ;
    sh:message "The OrganisationRole MUST link to exactly one era:Body representing the applicant."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 .


era-sh:DC2TCreatorOrganisationRole
    a sh:PropertyShape ;
    sh:path era:hasOrganisationRole  ;
    era:affectedProperty era:hasOrganisationRole  ;
    sh:name "Applicant Role" ;
    sh:nodeKind sh:IRI ;
    sh:description "Must be the 'Applicant' role from the ERA controlled vocabulary." ;
    sh:hasValue era-organisation-roles:Applicant ;
    sh:message "The creator OrganisationRole MUST have the role 'Applicant'."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 .


era-sh:DC2TContributor
    a sh:PropertyShape ;
    sh:path dcterms:contributor  ;
    era:affectedProperty dcterms:contributor  ;
    sh:name "Contributing Organizations" ;
    sh:class era:OrganisationRole ;
    sh:nodeKind sh:BlankNodeOrIRI ;
    sh:description "Organizations contributing to the Declaration (e.g., Notified Bodies, assessment bodies)." ;
    sh:message "Contributing organizations SHOULD be specified as era:OrganisationRole instances."@en ;
    sh:severity sh:Warning ;
    sh:minCount 0 ;
    sh:node [
        sh:property era-sh:DC2TContributorRoleOf ;
        sh:property era-sh:DC2TContributorOrganisationRole
    ] .

era-sh:DC2TContributorRoleOf
    a sh:PropertyShape ;
    sh:path era:roleOf  ;
    era:affectedProperty era:roleOf  ;
    sh:name "Contributing Body IRI" ;
    sh:nodeKind sh:IRI ;
    sh:class era:Body ;
    sh:description "The legal entity (era:Body) acting as contributor." ;
    sh:message "The contributor OrganisationRole MUST link to exactly one era:Body."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 .

era-sh:DC2TContributorOrganisationRole
    a sh:PropertyShape ;
    sh:path era:hasOrganisationRole  ;
    era:affectedProperty era:hasOrganisationRole  ;
    sh:name "Contributor Role" ;
    sh:nodeKind sh:IRI ;
    sh:description "Role from ERA controlled vocabulary (e.g., NoBo, DeBo, AssessmentBody)." ;
    sh:in (
        era-organisation-roles:NoBo
        era-organisation-roles:DeBo
        era-organisation-roles:AssessmentBody
        era-organisation-roles:CAB
    ) ;
    sh:message "The contributor MUST have a valid role (NoBo, DeBo, AssessmentBody, or CAB)."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 .

# ----------------------------------------------------------------------------
# 4. Technical Scope of the DC2T
# ----------------------------------------------------------------------------

era-sh:DC2TCoverage
    a sh:PropertyShape ;
    sh:path dcterms:coverage  ;
    era:affectedProperty dcterms:coverage  ;
    sh:name "Vehicle Type(s) covered by this Declaration" ;
    sh:nodeKind sh:IRI ;
    sh:class era:VehicleType ;
    sh:description "The Vehicle Type(s) as declared in scope of this Declaration" ;
    sh:message "Declaration MUST reference at least one registered vehicle Type."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 .

era-sh:DC2TSubject
    a sh:PropertyShape ;
    sh:path dcterms:subject  ;
    era:affectedProperty dcterms:subject  ;
    sh:name "Subject of Declaration (Vehicles or VehiclesSet)" ;
    sh:nodeKind sh:IRI ;
    sh:or (
        [ sh:class era:Vehicle ]
        [ sh:class era:VehicleSet ]
    ) ;
    sh:description "The specific (set of) vehicles which is the subject of the DC2T." ;
    sh:message "Declaration MUST declare conformity to at least one vehicle per covered vehicle type."@en ;
    sh:severity sh:Violation ;
    skos:editorialNote "SHACL-rule fails to protect against missing vehicles for a certain type." ;
    sh:minCount 1 .

# ----------------------------------------------------------------------------
# 5. Descriptive Metadata
# ----------------------------------------------------------------------------

era-sh:DC2TLabel
    a sh:PropertyShape ;
    sh:path rdfs:label  ;
    era:affectedProperty rdfs:label  ;
    sh:name "Declaration Title" ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:description "Brief title describing the subject of the declaration (e.g., project name of C2T)." ;
    sh:message "Declaration MUST have a human-readable title (rdfs:label)."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 .

era-sh:DC2TComment
    a sh:PropertyShape ;
    sh:path rdfs:comment  ;
    era:affectedProperty rdfs:comment  ;
    sh:name "Additional Comments" ;
    sh:or ( 
        [ sh:datatype xsd:string ] 
        [ sh:datatype rdf:langString ] 
    ) ;
    sh:nodeKind sh:Literal ;
    sh:description "Free-text comment providing additional information about the declaration." ;
    sh:message "Declaration SHOULD include a comment for additional context."@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 ;
    sh:maxCount 1 .

era-sh:DC2TSeeAlso
    a sh:PropertyShape ;
    sh:path rdfs:seeAlso  ;
    era:affectedProperty rdfs:seeAlso  ;
    sh:name "Reference URL" ;
    sh:datatype xsd:anyURI ;
    sh:nodeKind sh:Literal ;
    sh:description "Link to the Declaration document in a public database." ;
    sh:message "Declaration MAY include an URL"@en ;
    sh:severity sh:Warning ;
    sh:minCount 0 ;
    sh:maxCount 1 .

# ----------------------------------------------------------------------------
# 5b. Supporting Evidence
# ----------------------------------------------------------------------------
# Declarations are NOT supported by evidence from Conformity Assessment Bodies (CABs).


# ----------------------------------------------------------------------------
# 6. Versioning and Lifecycle
# ----------------------------------------------------------------------------

era-sh:DC2TReplaces
    a sh:PropertyShape ;
    sh:path dcterms:replaces  ;
    era:affectedProperty dcterms:replaces  ;
    sh:name "Replaced Declaration" ;
    sh:nodeKind sh:IRI ;
    sh:class era:ECDeclaration ;
    sh:description "Reference to the previous version of this Declaration, if this is a revised version." ;
    sh:message "If present, dcterms:replaces MUST reference another era:ECDeclaration."@en ;
    sh:severity sh:Info ;
    sh:maxCount 1 .

era-sh:DC2TStatus
    a sh:PropertyShape ;
    sh:path era:state  ;
    era:affectedProperty era:state  ;
    sh:name "Declaration Status" ;
    sh:nodeKind sh:IRI ;
    sh:class skos:Concept ;
    sh:description "Current lifecycle status of the declaration (e.g., valid, revoked, superseded)." ;
    sh:message "Declaration SHOULD have a status indicating its current validity (valid/revoked/superseded)."@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 ;
    sh:maxCount 1 .

# ----------------------------------------------------------------------------
# 7. SPARQL Constraints for Cross-Property Validation
# ----------------------------------------------------------------------------

era-sh:DC2TReplacementVersionSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "If dcterms:replaces is present, this Declaration MUST have a higher vpa:versionNumber than the replaced declaration."@en ;
    sh:select """
        PREFIX vpa: <https://w3id.org/vpa#>
        PREFIX dcterms: <http://purl.org/dc/terms/>

        SELECT $this ?replaced ?thisVersion ?replacedVersion WHERE {
            $this dcterms:replaces ?replaced ;
                  vpa:versionNumber ?thisVersion .
            ?replaced vpa:versionNumber ?replacedVersion .
            FILTER(?thisVersion <= ?replacedVersion)
        }
    """ .

era-sh:DC2TReplacementDateSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "If dcterms:replaces is present, this Declaration MUST have a later dcterms:issued date than the replaced declaration."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>

        SELECT $this ?replaced ?thisIssued ?replacedIssued WHERE {
            $this dcterms:replaces ?replaced ;
                  dcterms:issued ?thisIssued .
            ?replaced dcterms:issued ?replacedIssued .
            FILTER(?thisIssued <= ?replacedIssued)
        }
    """ .

# ============================================================================
# ERADIS EC Declaration Shapes
# ============================================================================
# This file defines SHACL shapes for validating EC Declarations of Conformity
# and EC Declarations of Suitability for Use as defined in:
# - Directive (EU) 2016/797 (Interoperability Directive)
# - Regulation (EU) 402/2013 (Common Safety Method on Risk Evaluation)
# - Decision 768/2008/EC (Common framework for marketing of products)
#
# EC Declarations are formal statements by manufacturers declaring that
# interoperability constituents (ICs) or subsystems comply with applicable
# EU legislation and harmonized standards.
#
# Two main types:
# 1. EC Declaration of Conformity (ECDoC): For interoperability constituents
# 2. EC Declaration of Suitability for Use (ECDoSU): For subsystems
# ============================================================================


# ----------------------------------------------------------------------------
# Main EC Declaration Shape
# ----------------------------------------------------------------------------
# Validates EC Declarations of Conformity (ECDoC) and Suitability for Use (ECDoSU).
# Uses sh:SPARQLTarget (SHACL-AF) to target only era:ECDeclaration instances
# with dcterms:type ECDoC or ECDoSU (excludes other ECDeclaration types).

era-sh:ECDeclarationShape 
    a sh:NodeShape ;
    sh:target [
        a sh:SPARQLTarget ;
        sh:select """
            SELECT ?this WHERE {
                ?this a <http://data.europa.eu/949/ECDeclaration> ;
                      <http://purl.org/dc/terms/type> ?type .
                FILTER(?type IN (
                    <http://data.europa.eu/949/concepts/ecd-types/ECDoC>,
                    <http://data.europa.eu/949/concepts/ecd-types/ECDoSU>
                ))
            }
        """
    ] ;

    rdfs:label "EC Declaration" ;
    rdfs:comment "Validates EC Declarations of Conformity and Suitability for Use according to EU Regulation 2016/797."@en ;

    # ----------- Shape document management
    dcterms:creator [ 
      foaf:name "Maarten Duhoux" ;
      dcterms:source <https://orcid.org/0009-0003-6653-6123> ;
      org:memberOf <http://publications.europa.eu/resource/authority/corporate-body/ERA>
    ] ;
    rdfs:seeAlso "https://eradis.era.europa.eu/interop_docs/ecDecl/default.aspx?DocumentType=ECDeclVer"^^xsd:anyURI ;
    rdfs:seeAlso "https://eradis.era.europa.eu/interop_docs/ecDecl/default.aspx?DocumentType=ECDeclCnf"^^xsd:anyURI ;
    rdfs:seeAlso "https://eradis.era.europa.eu/interop_docs/ecDecl/default.aspx?DocumentType=ECDeclSuit"^^xsd:anyURI ;
    owl:versionInfo "3.3.0" ;

    dcterms:created "2025-10-11"^^xsd:date ;
    # dcterms:modified ;
    
    # Core identification properties
    sh:property era-sh:ECDIdentifier ;
    sh:property era-sh:ECDType ;
    sh:property era-sh:ECDVersionNumber ;
    
    # Temporal properties
    sh:property era-sh:ECDCreated ;
    sh:property era-sh:ECDIssued ;
    sh:property era-sh:ECDAvailable ;
    
    # Organizational relationships
    sh:property era-sh:ECDCreator ;
    sh:property era-sh:ECDContributor ;
    
    # Legal and technical scope
    sh:property era-sh:ECDCoverage ;
    sh:property era-sh:ECDSubject ;
    
    # Descriptive metadata
    sh:property era-sh:ECDLabel ;
    sh:property era-sh:ECDComment ;
    sh:property era-sh:ECDSeeAlso ;
    
    # Versioning and lifecycle
    sh:property era-sh:ECDReplaces ;
    sh:property era-sh:ECDStatus ;
    
    # Supporting evidence
    sh:property era-sh:ECDRequires ;
    
    # SPARQL constraints
    sh:sparql era-sh:ECDReplacementVersionSPARQL ;
    sh:sparql era-sh:ECDReplacementDateSPARQL ;
    sh:sparql era-sh:ECDEvidenceConsistencySPARQL .

# ----------------------------------------------------------------------------
# 1. Core Identification Properties
# ----------------------------------------------------------------------------

era-sh:ECDIdentifier
    a sh:PropertyShape ;
    sh:path dcterms:identifier  ;
    era:affectedProperty dcterms:identifier  ;
    sh:name "EC Declaration Identifier" ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:description "Unique identifier for the EC Declaration in format: CC/NNNNNNNNNNNNNN/YYYY/NNNNNN where CC=country code, N=alphanumeric, YYYY=year." ;
    sh:pattern "^[A-Z]{2}/[A-Z0-9]{14}/[0-9]{4}/[0-9]{6}$" ;
    sh:message "EC Declaration identifier MUST follow the pattern CC/NNNNNNNNNNNNNN/YYYY/NNNNNN (e.g., AT/000000FN93653a/2021/001714)."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 .

era-sh:ECDType
    a sh:PropertyShape ;
    sh:path dcterms:type  ;
    era:affectedProperty dcterms:type  ;
    sh:name "EC Declaration Type" ;
    sh:nodeKind sh:IRI ;
    sh:description "Type of EC Declaration: ECDoC (Conformity) or ECDoSU (Suitability for Use)." ;
    sh:in ( era-ecd-types:ECDoC era-ecd-types:ECDoSU ) ;
    sh:message "EC Declaration type MUST be either ECDoC (Declaration of Conformity) or ECDoSU (Declaration of Suitability for Use)."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 .

era-sh:ECDVersionNumber
    a sh:PropertyShape ;
    sh:path vpa:versionNumber  ;
    era:affectedProperty vpa:versionNumber  ;
    sh:name "Version Number" ;
    sh:datatype xsd:integer ;
    sh:nodeKind sh:Literal ;
    sh:description "Sequential version number of this EC Declaration (starts at 1, increments with each revision)." ;
    sh:minInclusive 1 ;
    sh:message "Version number MUST be a positive integer starting from 1."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 .

# ----------------------------------------------------------------------------
# 2. Temporal Properties
# ----------------------------------------------------------------------------

era-sh:ECDCreated
    a sh:PropertyShape ;
    sh:path dcterms:created  ;
    era:affectedProperty dcterms:created  ;
    sh:name "Creation Date in ERADIS+" ;
    sh:datatype xsd:date ;
    sh:nodeKind sh:Literal ;
    sh:description "Date when this EC Declaration resource was created in the ERADIS+ knowledge graph." ;
    sh:message "Knowledge graph creation date MUST be present as xsd:date."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 .

era-sh:ECDIssued
    a sh:PropertyShape ;
    sh:path dcterms:issued  ;
    era:affectedProperty dcterms:issued  ;
    sh:name "Document Issue Date" ;
    sh:datatype xsd:date ;
    sh:nodeKind sh:Literal ;
    sh:description "Official date when the manufacturer issued this EC Declaration document." ;
    sh:message "Document issue date MUST be present as xsd:date."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 .

era-sh:ECDAvailable
    a sh:PropertyShape ;
    sh:path dcterms:available  ;
    era:affectedProperty dcterms:available  ;
    sh:name "ERADIS Availability Date" ;
    sh:datatype xsd:date ;
    sh:nodeKind sh:Literal ;
    sh:description "Date when this EC Declaration became available in the ERADIS public database." ;
    sh:message "ERADIS availability date SHOULD be present as xsd:date."@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 ;
    sh:maxCount 1 .

# ----------------------------------------------------------------------------
# 3. Organizational Relationships
# ----------------------------------------------------------------------------

era-sh:ECDCreator
    a sh:PropertyShape ;
    sh:path dcterms:creator  ;
    era:affectedProperty dcterms:creator  ;
    sh:name "Manufacturer (Declaration Issuer)" ;
    sh:class era:OrganisationRole ;
    sh:nodeKind sh:BlankNodeOrIRI ;
    sh:description "The manufacturer issuing this EC Declaration (must be an era:OrganisationRole with role 'Manufacturer')." ;
    sh:message "EC Declaration MUST have exactly one manufacturer as creator (era:OrganisationRole with role Manufacturer)."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:node [
        sh:property era-sh:ECDCreatorRoleOf ;
        sh:property era-sh:ECDCreatorOrganisationRole
] .


era-sh:ECDCreatorRoleOf
        a sh:PropertyShape ;
        sh:path era:roleOf  ;
        era:affectedProperty era:roleOf  ;
        sh:name "Manufacturer Body IRI" ;
        sh:nodeKind sh:IRI ;
        sh:class era:Body ;
        sh:description "The legal entity (era:Body) acting as manufacturer." ;
        sh:message "The OrganisationRole MUST link to exactly one era:Body representing the manufacturer."@en ;
        sh:severity sh:Violation ;
        sh:minCount 1 ;
        sh:maxCount 1 .

era-sh:ECDCreatorOrganisationRole
        a sh:PropertyShape ;
        sh:path era:hasOrganisationRole  ;
        era:affectedProperty era:hasOrganisationRole  ;
        sh:name "Manufacturer Role" ;
        sh:nodeKind sh:IRI ;
        sh:description "Must be the 'Manufacturer' role from the ERA controlled vocabulary." ;
        sh:hasValue era-organisation-roles:Manufacturer ;
        sh:message "The creator OrganisationRole MUST have the role 'Manufacturer'."@en ;
        sh:severity sh:Violation ;
        sh:minCount 1 ;
        sh:maxCount 1 .

era-sh:ECDContributor
    a sh:PropertyShape ;
    sh:path dcterms:contributor  ;
    era:affectedProperty dcterms:contributor  ;
    sh:name "Contributing Organizations" ;
    sh:class era:OrganisationRole ;
    sh:nodeKind sh:BlankNodeOrIRI ;
    sh:description "Organizations contributing to the EC Declaration (e.g., Notified Bodies, assessment bodies)." ;
    sh:message "Contributing organizations SHOULD be specified as era:OrganisationRole instances."@en ;
    sh:severity sh:Warning ;
    sh:minCount 0 ;
    sh:node [
        sh:property era-sh:ECDContributorRoleOf ;
        sh:property era-sh:ECDContributorOrganisationRole
] .


era-sh:ECDContributorRoleOf
    a sh:PropertyShape ;
    sh:path era:roleOf  ;
    era:affectedProperty era:roleOf  ;
    sh:name "Contributing Body IRI" ;
    sh:nodeKind sh:IRI ;
    sh:class era:Body ;
    sh:description "The legal entity (era:Body) acting as contributor." ;
    sh:message "The contributor OrganisationRole MUST link to exactly one era:Body."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1
.

era-sh:ECDContributorOrganisationRole
    a sh:PropertyShape ;
    sh:path era:hasOrganisationRole  ;
    era:affectedProperty era:hasOrganisationRole  ;
    sh:name "Contributor Role" ;
    sh:nodeKind sh:IRI ;
    sh:description "Role from ERA controlled vocabulary (e.g., NoBo, DeBo, AssessmentBody)." ;
    sh:in (
                era-organisation-roles:NoBo
                era-organisation-roles:DeBo
                era-organisation-roles:AssessmentBody
                era-organisation-roles:CAB
            ) ;
    sh:message "The contributor MUST have a valid role (NoBo, DeBo, AssessmentBody, or CAB)."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1
.

# ----------------------------------------------------------------------------
# 4. Legal and Technical Scope
# ----------------------------------------------------------------------------

era-sh:ECDCoverage
    a sh:PropertyShape ;
    sh:path dcterms:coverage  ;
    era:affectedProperty dcterms:coverage  ;
    sh:name "Applicable EU Legislation" ;
    sh:nodeKind sh:IRI ;
    sh:class vpa:Requirement ;
    sh:description "EU Directives, Regulations, or Decisions covered by this declaration (references to ERALEX)." ;
    sh:message "EC Declaration MUST reference at least one applicable EU legal instrument (Directive/Regulation/Decision)."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 .

era-sh:ECDSubject
    a sh:PropertyShape ;
    sh:path dcterms:subject  ;
    era:affectedProperty dcterms:subject  ;
    sh:name "Subject of Declaration (IC/Subsystem)" ;
    sh:nodeKind sh:IRI ;
    sh:or (
        [ sh:class era:IC ]
        [ sh:class era:TSI ]
        [ sh:class vpa:Requirement ]
    ) ;
    sh:description "The specific Interoperability Constituent (IC), TSI section, or requirement that this declaration addresses." ;
    sh:message "EC Declaration MUST declare conformity to at least one IC, TSI, or specific requirement."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 .

# ----------------------------------------------------------------------------
# 5. Descriptive Metadata
# ----------------------------------------------------------------------------

era-sh:ECDLabel
    a sh:PropertyShape ;
    sh:path rdfs:label  ;
    era:affectedProperty rdfs:label  ;
    sh:name "Declaration Title" ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:description "Brief title describing the subject of the declaration (e.g., product name or component type)." ;
    sh:message "EC Declaration MUST have a human-readable title (rdfs:label)."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 .

era-sh:ECDComment
    a sh:PropertyShape ;
    sh:path rdfs:comment  ;
    era:affectedProperty rdfs:comment  ;
    sh:name "Additional Comments" ;
    sh:or ( 
        [ sh:datatype xsd:string ] 
        [ sh:datatype rdf:langString ] 
    ) ;
    sh:nodeKind sh:Literal ;
    sh:description "Free-text comment providing additional information about the declaration." ;
    sh:message "EC Declaration SHOULD include a comment for additional context."@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 ;
    sh:maxCount 1 .

era-sh:ECDSeeAlso
    a sh:PropertyShape ;
    sh:path rdfs:seeAlso  ;
    era:affectedProperty rdfs:seeAlso  ;
    sh:name "ERADIS Reference URL" ;
    sh:datatype xsd:anyURI ;
    sh:nodeKind sh:Literal ;
    sh:description "Link to the EC Declaration document in the ERADIS public database." ;
    sh:pattern "^https://eradis\\.era\\.europa\\.eu/interop_docs/ecDecl/view\\.aspx\\?id=[0-9]+$" ;
    sh:message "EC Declaration SHOULD include an ERADIS URL following the pattern: https://eradis.era.europa.eu/interop_docs/ecDecl/view.aspx?id=NNNNN"@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 ;
    sh:maxCount 1 .

# ----------------------------------------------------------------------------
# 5b. Supporting Evidence
# ----------------------------------------------------------------------------
# EC Declarations must be supported by evidence from Conformity Assessment Bodies (CABs).
# This evidence consists of test reports and certificates referenced via the CLD system.

era-sh:ECDRequires
    a sh:PropertyShape ;
    sh:path dcterms:requires  ;
    era:affectedProperty dcterms:requires  ;
    sh:name "Supporting Evidence" ;
    sh:class era:CABEvidence ;
    sh:nodeKind sh:BlankNodeOrIRI ;
    sh:description "Evidence from Conformity Assessment Bodies (CABs) supporting this EC Declaration. Each evidence set references test reports and certificates via CLD identifiers." ;
    sh:message "EC Declaration MUST be supported by at least one set of CAB evidence (test reports, certificates)."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:node era-sh:CABEvidenceShape .

# Validates the structure of CAB evidence sets
era-sh:CABEvidenceShape
    a sh:NodeShape ;
    sh:targetClass era:CABEvidence ;
    sh:nodeKind sh:IRI ;
    rdfs:label "CAB Evidence Set" ;
    rdfs:comment "A collection of supporting documents (test reports, certificates) from a Conformity Assessment Body." ;
    sh:property era-sh:CABEvidenceDocuments .

era-sh:CABEvidenceDocuments
    a sh:PropertyShape ;
    sh:path rdfs:member  ;
    era:affectedProperty rdfs:member  ;
    sh:name "Evidence Documents" ;
    sh:nodeKind sh:BlankNodeOrIRI ;
    sh:description "Individual CLD references (Certification Level Document entries) for test reports and certificates." ;
    sh:message "CAB Evidence MUST contain at least one CLD reference (test report or certificate)."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:node era-sh:CLDReferenceValueShape .

# Validates individual CLD references within evidence sets
era-sh:CLDReferenceShape
    a sh:NodeShape ;
    sh:targetClass era:CertificationLevelDocument ;
    sh:nodeKind sh:IRI ;
    rdfs:label "CLD Reference" ;
    rdfs:comment "A reference to a test report or certificate in the Certification Level Document."@en .

era-sh:CLDReferenceValueShape
    a sh:NodeShape ;
    sh:or (
          [
              sh:nodeKind sh:IRI ;
              sh:class era:CertificationLevelDocument
          ]
          [
              sh:nodeKind sh:BlankNode ;
              sh:property era-sh:CLDReferenceIdentifier
          ]
      ) ;
    sh:message "CLD reference MUST be either a direct IRI to an existing CLD resource OR a blank node with a valid CLD identifier."@en .

era-sh:CLDReferenceIdentifier
    a sh:PropertyShape ;
    sh:path dcterms:identifier  ;
    era:affectedProperty dcterms:identifier  ;
    sh:name "CLD Identifier" ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:description "CLD identifier in format: NNNN/N/CCN/YYYY/AAA/LL/NNNN[/VNN] where NNNN=NoBo number, N=type, CC=country, YYYY=year, AAA=abbreviation, LL=language, NNNN=sequential, VNN=version." ;
    sh:pattern "^[0-9]{4}/[0-9]/[A-Z]{2}[0-9]/[0-9]{4}/[A-Z]{3}/[A-Z]{2}/[0-9]{4}(/V[0-9]+)?$" ;
    sh:message "CLD identifier MUST follow the pattern NNNN/N/CCN/YYYY/AAA/LL/NNNN[/VNN] (e.g., 1714/2/CH1/2020/RST/EN/3474 or 1714/4/CH1/2020/RST/EN/3451/V15)."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 .

# ----------------------------------------------------------------------------
# 6. Versioning and Lifecycle
# ----------------------------------------------------------------------------

era-sh:ECDReplaces
    a sh:PropertyShape ;
    sh:path dcterms:replaces  ;
    era:affectedProperty dcterms:replaces  ;
    sh:name "Replaced Declaration" ;
    sh:nodeKind sh:IRI ;
    sh:class era:ECDeclaration ;
    sh:description "Reference to the previous version of this EC Declaration, if this is a revised version." ;
    sh:message "If present, dcterms:replaces MUST reference another era:ECDeclaration."@en ;
    sh:severity sh:Info ;
    sh:maxCount 1 .

era-sh:ECDStatus
    a sh:PropertyShape ;
    sh:path era:status  ;
    era:affectedProperty era:state  ;
    sh:name "Declaration Status" ;
    sh:nodeKind sh:IRI ;
    sh:class skos:Concept ;
    sh:description "Current lifecycle status of the declaration (e.g., valid, revoked, superseded)." ;
    sh:message "EC Declaration SHOULD have a status indicating its current validity (valid/revoked/superseded)."@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 ;
    sh:maxCount 1 .

# ----------------------------------------------------------------------------
# 7. SPARQL Constraints for Cross-Property Validation
# ----------------------------------------------------------------------------

era-sh:ECDReplacementVersionSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "If dcterms:replaces is present, this EC Declaration MUST have a higher vpa:versionNumber than the replaced declaration."@en ;
    sh:select """
        PREFIX vpa: <https://w3id.org/vpa#>
        PREFIX dcterms: <http://purl.org/dc/terms/>

        SELECT $this ?replaced ?thisVersion ?replacedVersion WHERE {
            $this dcterms:replaces ?replaced ;
                  vpa:versionNumber ?thisVersion .
            ?replaced vpa:versionNumber ?replacedVersion .
            FILTER(?thisVersion <= ?replacedVersion)
        }
    """ .

era-sh:ECDReplacementDateSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "If dcterms:replaces is present, this EC Declaration MUST have a later dcterms:issued date than the replaced declaration."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>

        SELECT $this ?replaced ?thisIssued ?replacedIssued WHERE {
            $this dcterms:replaces ?replaced ;
                  dcterms:issued ?thisIssued .
            ?replaced dcterms:issued ?replacedIssued .
            FILTER(?thisIssued <= ?replacedIssued)
        }
    """ .

era-sh:ECDEvidenceConsistencySPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Warning ;
    sh:message "All CLD references within a single CABEvidence set SHOULD originate from the same Notified Body (same NoBo number prefix)."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>
        PREFIX era: <http://data.europa.eu/949/>
        PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>

        SELECT $this ?evidence ?member1 ?id1 ?member2 ?id2 WHERE {
            $this dcterms:requires ?evidence .
            ?evidence a era:CABEvidence ;
                      rdfs:member ?member1 , ?member2 .
            ?member1 dcterms:identifier ?id1 .
            ?member2 dcterms:identifier ?id2 .
            
            # Extract NoBo number (first 4 digits before first slash)
            BIND(SUBSTR(?id1, 1, 4) AS ?nobo1)
            BIND(SUBSTR(?id2, 1, 4) AS ?nobo2)
            
            FILTER(?member1 != ?member2 && ?nobo1 != ?nobo2)
        }
    """ .


#################################################################
#
#  PROV-O Lifecycle Shapes for Evidence Documents & Permissions
#
#  This file defines SHACL shapes for the provenance modelling of
#  lifecycle events on any subclass of vpa:EvidenceDocument or
#  vpa:Permission — in practice era:CertificationLevelDocument
#  (CLD) and era:Certificate, which share the same activity
#  patterns.
#
#  Shape names use the prefix "EDP" (Evidence Document /
#  Permission) to reflect this broader scope.
#
#  ┌──────────────────────────────────────────────────────────┐
#  │ Group 0 — State-reversing activity (EDPUndo)            │
#  │   • Reverses the effect of the last Group 1 activity    │
#  │   • Linked to the reversed activity via                 │
#  │     prov:wasInformedBy                                  │
#  │   • Only certain types are undoable (see § 2 SPARQL)    │
#  │     ✓ EDPRequestRevoke, EDPRequestSubmit, EDPRevoke,    │
#  │       EDPWithdraw, EDPSuspend, EDPReject, EDPUndo       │
#  │     ✗ EDPAmend, EDPCorrect (chain a new one instead)    │
#  ├──────────────────────────────────────────────────────────┤
#  │ Group 1 — State-changing activities                     │
#  │   (amend, correct, request revocation, revoke,          │
#  │    withdraw, suspend, reject)                           │
#  │   • No new resource is minted                           │
#  │   • The Activity prov:used the existing Entity           │
#  │   • era:state on the Entity changes accordingly         │
#  │   • For revoke/withdraw: Entity prov:wasInvalidatedBy   │
#  │                                                         │
#  │   To preserve history of changed property values, a     │
#  │   lightweight "before-snapshot" (prov:Entity with        │
#  │   prov:specializationOf the main entity) captures the   │
#  │   old values. The activity invalidates the before-       │
#  │   snapshot and generates an after-snapshot. Both carry   │
#  │   only the CHANGED properties — no full entity needed.  │
#  │                                                         │
#  │   mainEDP ◄── prov:specializationOf ── beforeSnapshot   │
#  │   mainEDP ◄── prov:specializationOf ── afterSnapshot    │
#  │   activity ── prov:used ────────────── beforeSnapshot   │
#  │   activity ── prov:generated ───────── afterSnapshot    │
#  ├──────────────────────────────────────────────────────────┤
#  │ Group 2 — Versioning activities                         │
#  │   (request submit, submit, replace, renew)              │
#  │   • A new resource is minted (new URI)                  │
#  │   • newEntity prov:wasDerivedFrom oldEntity             │
#  │     (except EDPSubmit/EDPRequestSubmit — no parent)     │
#  │   • newEntity prov:wasGeneratedBy thisActivity          │
#  │   • oldEntity dcterms:isReplacedBy newEntity            │
#  │   • EDPRequestSubmit: resource enters "submitted"       │
#  │     state; a follow-up EDPSubmit validates it           │
#  └──────────────────────────────────────────────────────────┘
#
#  Request-type activities (EDPRequestRevoke, EDPRequestSubmit)
#  are linked to their fulfilment action via skos:related.
#  Whether a given resource type requires the request step is
#  governed by the resource-specific SHACL shape, not this file.
#
#  The SKOS concept scheme era-edp-act:EDPActivityTypes enumerates
#  the allowed dcterms:type values for these activities.
#
#  See also:  PROV.md  (examples & diagrams)
#
#################################################################


    # ----------- Shape document management
    # dcterms:creator [ 
    #   foaf:name "Maarten Duhoux" ;
    #   dcterms:source <https://orcid.org/0009-0003-6653-6123> ;
    #   org:memberOf <http://publications.europa.eu/resource/authority/corporate-body/ERA>
    # ] ;
    # owl:versionInfo "3.3.0" ;
    # dcterms:created "2026-02-13"^^xsd:date ;
    # dcterms:modified "2026-02-23"^^xsd:date ;



#################################################################
#
#    1. EDP as prov:Entity
#
#    Every CLD or Certificate can optionally carry PROV-O entity
#    properties (wasGeneratedBy, wasInvalidatedBy, wasDerivedFrom,
#    etc.). This shape validates those when present.
#
#################################################################


era-sh:EDPEntityShape
    a sh:NodeShape ;
    sh:targetClass era:CertificationLevelDocument , era:Certificate ;
    sh:nodeKind sh:IRI ;
    rdfs:label "Evidence Document / Permission as PROV Entity"@en ;
    rdfs:comment """Validates PROV-O entity properties on a CLD or Certificate
    (both subclasses of vpa:EvidenceDocument / vpa:Permission).
    Both are implicitly prov:Entities. This shape checks that provenance
    links (wasGeneratedBy, wasInvalidatedBy, wasDerivedFrom) are consistent
    when present."""@en ;

    sh:property era-sh:EDPEntityWasGeneratedBy ;
    sh:property era-sh:EDPEntityWasInvalidatedBy ;
    sh:property era-sh:EDPEntityWasDerivedFrom ;
    sh:property era-sh:EDPEntityWasAttributedTo ;
    sh:property era-sh:EDPEntityGeneratedAtTime ;
    sh:property era-sh:EDPStatePropertyShape ;

    # --- SPARQL: wasDerivedFrom ↔ dcterms:replaces consistency ---
    sh:sparql era-sh:DerivedFromReplacesSPARQL ;

    # --- SPARQL: wasInvalidatedBy → state must be revoked/withdrawn/suspended/rejected ---
    sh:sparql era-sh:InvalidatedStateSPARQL .


# --- prov:wasGeneratedBy (the Activity that created this entity) ---
era-sh:EDPEntityWasGeneratedBy
        a sh:PropertyShape ;
        sh:path prov:wasGeneratedBy  ;
        era:affectedProperty prov:wasGeneratedBy  ;
        sh:name "Generated by Activity"@en ;
        sh:nodeKind sh:IRI ;
        sh:class prov:Activity ;
        sh:description "The prov:Activity that generated (created) this entity. Present when it was produced by a versioning activity (EDPSubmit, EDPReplace, EDPRenew)."@en ;
        sh:message "(Info) No prov:Activity recorded as having generated this entity."@en ;
        sh:severity sh:Info ;
        sh:maxCount 1 .

# --- prov:wasInvalidatedBy (the Activity that invalidated this entity) ---
era-sh:EDPEntityWasInvalidatedBy
        a sh:PropertyShape ;
        sh:path prov:wasInvalidatedBy  ;
        era:affectedProperty prov:wasInvalidatedBy  ;
        sh:name "Invalidated by Activity"@en ;
        sh:nodeKind sh:IRI ;
        sh:class prov:Activity ;
        sh:description "The prov:Activity that invalidated this entity (revocation, withdrawal, or replacement by a newer version)."@en ;
        sh:message "(Info) No prov:Activity recorded as having invalidated this entity."@en ;
        sh:severity sh:Info ;
        sh:maxCount 1 .

# --- prov:wasDerivedFrom (the parent entity this one was derived from) ---
era-sh:EDPEntityWasDerivedFrom
        a sh:PropertyShape ;
        sh:path prov:wasDerivedFrom  ;
        era:affectedProperty prov:wasDerivedFrom  ;
        sh:name "Derived from parent entity"@en ;
        sh:nodeKind sh:IRI ;
        sh:or (
            [ sh:class era:CertificationLevelDocument ]
            [ sh:class era:Certificate ]
        ) ;
        sh:description "The parent CLD or Certificate from which this one was derived (i.e. replaced). This is the inverse provenance view of dcterms:isReplacedBy."@en ;
        sh:message "(Info) This entity does not declare a parent it was derived from."@en ;
        sh:severity sh:Info ;
        sh:maxCount 1 .

# --- prov:wasAttributedTo (the Agent responsible for this entity) ---
era-sh:EDPEntityWasAttributedTo
        a sh:PropertyShape ;
        sh:path prov:wasAttributedTo  ;
        era:affectedProperty prov:wasAttributedTo  ;
        sh:name "Attributed to Agent"@en ;
        sh:nodeKind sh:IRI ;
        sh:description "The prov:Agent (typically an era:Body or era:OrganisationRole) to whom the entity is attributed."@en ;
        sh:message "(Info) No prov:Agent is attributed to this entity."@en ;
        sh:severity sh:Info .

# --- prov:generatedAtTime (shortcut for creation timestamp as Entity) ---
era-sh:EDPEntityGeneratedAtTime
        a sh:PropertyShape ;
        sh:path prov:generatedAtTime  ;
        era:affectedProperty prov:generatedAtTime  ;
        sh:name "Entity generation time"@en ;
        sh:datatype xsd:dateTime ;
        sh:nodeKind sh:Literal ;
        sh:description "The point in time at which this entity (as prov:Entity) was generated. Should be consistent with dcterms:issued."@en ;
        sh:message "If prov:generatedAtTime is present, it MUST be an xsd:dateTime."@en ;
        sh:severity sh:Warning ;
        sh:maxCount 1 .

era-sh:DerivedFromReplacesSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Warning ;
    sh:message "This entity has `prov:wasDerivedFrom` {?parent}, but does not also declare `dcterms:replaces` pointing to the same parent, or vice versa."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>
        PREFIX prov: <http://www.w3.org/ns/prov#>

        SELECT $this ?parent WHERE {
          {
            $this prov:wasDerivedFrom ?parent .
            FILTER NOT EXISTS { $this dcterms:replaces ?parent }
          } UNION {
            $this dcterms:replaces ?parent .
            FILTER NOT EXISTS { $this prov:wasDerivedFrom ?parent }
          }
        }
    """ .

era-sh:InvalidatedStateSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Warning ;
    sh:message "This entity was invalidated by an Activity, but its era:state is not set to Revoked, Withdrawn, Suspended, or Rejected."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>
        PREFIX prov: <http://www.w3.org/ns/prov#>
        PREFIX era: <http://data.europa.eu/949/>
        PREFIX era-states-eratv: <http://data.europa.eu/949/concepts/states/eratv/>

        SELECT $this ?state WHERE {
          $this prov:wasInvalidatedBy ?act .
          OPTIONAL { $this era:state ?state }
          FILTER(
            !bound(?state) ||
            ?state NOT IN (
              era-states-eratv:revoked, era-states-eratv:withdrawn,
              era-states-eratv:suspended, era-states-eratv:rejected
            )
          )
        }
    """ .



#################################################################
#
#    2. Change Activity shape (prov:Activity)
#
#    Validates activities linked via era:changeActivity.
#    Every such activity MUST have a dcterms:type from the
#    era-edp-act:EDPActivityTypes SKOS scheme, a date, and must
#    prov:used at least one EDP or snapshot.
#
#################################################################


era-sh:EDPChangeActivityShape
    a sh:NodeShape ;
    sh:targetClass prov:Activity ;
    sh:nodeKind sh:IRI ;

    rdfs:label "EDP Change Activity"@en ;
    rdfs:comment """Validates a prov:Activity representing a lifecycle event on an
    Evidence Document or Permission (CLD / Certificate).
    Covers state-reversing activities (Group 0: undo), state-changing activities
    (Group 1: amend, correct, request revocation, revoke, withdraw, suspend, reject)
    and versioning activities (Group 2: submit, replace, renew) that mint a new entity."""@en ;

    sh:property era-sh:EDPChangeActivityType ;
    sh:property era-sh:EDPChangeActivityAtTime ;
    sh:property era-sh:EDPChangeActivityStartedAtTime ;
    sh:property era-sh:EDPChangeActivityWasAssociatedWith ;
    sh:property era-sh:EDPChangeActivityUsed ;
    sh:property era-sh:EDPChangeActivityGenerated ;
    sh:property era-sh:EDPChangeActivityState ;
    sh:property era-sh:EDPChangeActivityComment ;
    sh:property era-sh:EDPChangeActivityWasInformedBy ;

    # --- SPARQL: Versioning activities MUST generate a new entity ---
    sh:sparql era-sh:VersioningMustGenerateSPARQL ;

    # --- SPARQL: Group 0/1 activities MUST NOT mint a new full entity ---
    sh:sparql era-sh:StateChangeMustNotMintSPARQL ;

    # --- SPARQL: Non-submit activities MUST prov:used an existing entity ---
    sh:sparql era-sh:NonSubmitMustUseSPARQL ;

    # --- SPARQL: EDPUndo MUST reference the activity it reverses ---
    sh:sparql era-sh:UndoMustReferenceActivitySPARQL ;

    # --- SPARQL: EDPUndo target MUST be of an undoable type ---
    sh:sparql era-sh:UndoTargetMustBeUndoableSPARQL ;

    # --- SPARQL: prov:startedAtTime <= prov:atTime ---
    sh:sparql era-sh:ActivityTimeOrderSPARQL .


# --- dcterms:type — the kind of activity (from EDP Activity Types SKOS) ---
era-sh:EDPChangeActivityType
        a sh:PropertyShape ;
        sh:path dcterms:type  ;
        era:affectedProperty dcterms:type  ;
        sh:name "Activity type"@en ;
        sh:nodeKind sh:IRI ;
        sh:class skos:Concept ;
        era:inSkosConceptScheme era-edp-act:EDPActivityTypes ;
        sh:description "The type of lifecycle activity, from the EDPActivityTypes concept scheme."@en ;
        sh:message "The Activity MUST have a dcterms:type pointing to a skos:Concept from era-edp-act:EDPActivityTypes."@en ;
        sh:severity sh:Violation ;
        sh:minCount 1 ;
        sh:maxCount 1 .

# --- prov:atTime — when the activity took effect ---
era-sh:EDPChangeActivityAtTime
        a sh:PropertyShape ;
        sh:path prov:atTime  ;
        era:affectedProperty prov:atTime  ;
        sh:name "Activity effective date"@en ;
        sh:datatype xsd:dateTime ;
        sh:nodeKind sh:Literal ;
        sh:description "The date/time at which this activity concluded (took effect)."@en ;
        sh:message "The Activity MUST have a prov:atTime (xsd:dateTime)."@en ;
        sh:severity sh:Violation ;
        sh:minCount 1 ;
        sh:maxCount 1 .

# --- prov:startedAtTime — when the activity started (optional) ---
era-sh:EDPChangeActivityStartedAtTime
        a sh:PropertyShape ;
        sh:path prov:startedAtTime  ;
        era:affectedProperty prov:startedAtTime  ;
        sh:name "Activity start date"@en ;
        sh:datatype xsd:dateTime ;
        sh:nodeKind sh:Literal ;
        sh:description "The date/time at which this activity started, if different from the end time."@en ;
        sh:message "If present, prov:startedAtTime MUST be an xsd:dateTime."@en ;
        sh:severity sh:Warning ;
        sh:maxCount 1 .

# --- prov:wasAssociatedWith — who performed the activity ---
era-sh:EDPChangeActivityWasAssociatedWith
        a sh:PropertyShape ;
        sh:path prov:wasAssociatedWith  ;
        era:affectedProperty prov:wasAssociatedWith  ;
        sh:name "Associated agent"@en ;
        sh:nodeKind sh:IRI ;
        sh:description "The prov:Agent (person or organisation) who carried out this activity."@en ;
        sh:message "The Activity SHOULD declare who performed it via prov:wasAssociatedWith."@en ;
        sh:severity sh:Warning ;
        sh:minCount 1 .

# --- prov:used — the entity/snapshot consumed/acted upon ---
era-sh:EDPChangeActivityUsed
        a sh:PropertyShape ;
        sh:path prov:used  ;
        era:affectedProperty prov:used  ;
        sh:name "Entity acted upon"@en ;
        sh:nodeKind sh:IRI ;
        sh:description "The EDP or a before-snapshot (prov:Entity with prov:specializationOf) that this activity acted upon. Required for all activity types except EDPSubmit (which has no parent)."@en ;
        sh:message "(Info) This Activity did not declare a prov:used entity."@en ;
        sh:severity sh:Info .

# --- prov:generated — entity or snapshot produced ---
era-sh:EDPChangeActivityGenerated
        a sh:PropertyShape ;
        sh:path prov:generated  ;
        era:affectedProperty prov:generated  ;
        sh:name "Entity or snapshot produced"@en ;
        sh:nodeKind sh:IRI ;
        sh:description "For Group 2: the new EDP. For Group 0/1: an after-snapshot (prov:Entity with prov:specializationOf) carrying the updated property values."@en ;
        sh:message "(Info) This Activity did not generate any entity or snapshot."@en ;
        sh:severity sh:Info .

# --- era:state — the resulting state of the activity itself (if tracked) ---
era-sh:EDPChangeActivityState
        a sh:PropertyShape ;
        sh:path era:state  ;
        era:affectedProperty era:state  ;
        sh:name "Activity state"@en ;
        sh:nodeKind sh:IRI ;
        sh:class skos:Concept ;
        era:inSkosConceptScheme <http://data.europa.eu/949/concepts/states/States> ;
        sh:description "The state of the activity itself (e.g. submitted, in force)."@en ;
        sh:message "If present, era:state MUST be a skos:Concept from the States scheme."@en ;
        sh:severity sh:Warning ;
        sh:maxCount 1 .

# --- rdfs:comment — free-text justification ---
era-sh:EDPChangeActivityComment
        a sh:PropertyShape ;
        sh:path rdfs:comment  ;
        era:affectedProperty rdfs:comment  ;
        sh:name "Activity justification"@en ;
        sh:or ( [sh:datatype xsd:string ] [sh:datatype rdf:langString ] ) ;
        sh:nodeKind sh:Literal ;
        sh:description "Free-text explanation or justification for the activity."@en ;
        sh:message "The Activity SHOULD include a justification (rdfs:comment)."@en ;
        sh:severity sh:Warning ;
        sh:maxCount 1 .

# --- prov:wasInformedBy — the activity this undo reverses ---
era-sh:EDPChangeActivityWasInformedBy
        a sh:PropertyShape ;
        sh:path prov:wasInformedBy  ;
        era:affectedProperty prov:wasInformedBy  ;
        sh:name "Undo target activity"@en ;
        sh:nodeKind sh:IRI ;
        sh:class prov:Activity ;
        sh:description "For EDPUndo activities: the prov:Activity whose effect is being reversed. Uses prov:wasInformedBy because the undo activity acts upon the outcome of the previous activity."@en ;
        sh:message "(Info) prov:wasInformedBy not present (only required for EDPUndo)."@en ;
        sh:severity sh:Info ;
        sh:maxCount 1 .


era-sh:VersioningMustGenerateSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "This versioning activity (EDPRequestSubmit/EDPSubmit/EDPReplace/EDPRenew) MUST produce a new entity via prov:generated (unless EDPSubmit is validating an EDPRequestSubmit)."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>
        PREFIX prov: <http://www.w3.org/ns/prov#>
        PREFIX era-edp-act: <http://data.europa.eu/949/concepts/edp-activity-types/>

        SELECT $this WHERE {
          $this dcterms:type ?type .
          FILTER(?type IN (era-edp-act:EDPRequestSubmit, era-edp-act:EDPSubmit, era-edp-act:EDPReplace, era-edp-act:EDPRenew))
          FILTER NOT EXISTS { $this prov:generated ?newEntity }
          # Exception: EDPSubmit following EDPRequestSubmit is a validation
          # (state change only), not a minting activity.
          FILTER(
            ?type != era-edp-act:EDPSubmit
            || NOT EXISTS {
              $this prov:wasInformedBy ?reqAct .
              ?reqAct dcterms:type era-edp-act:EDPRequestSubmit
            }
          )
        }
    """ .

era-sh:StateChangeMustNotMintSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Warning ;
    sh:message "This activity ({?type}) generated a full CLD/Certificate {?newEntity} — only standalone versioning activities (EDPRequestSubmit/EDPSubmit/EDPReplace/EDPRenew) should mint new resources."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>
        PREFIX prov: <http://www.w3.org/ns/prov#>
        PREFIX era-edp-act: <http://data.europa.eu/949/concepts/edp-activity-types/>
        PREFIX era: <http://data.europa.eu/949/>

        SELECT $this ?type ?newEntity WHERE {
          {
            # Group 0/1 activities must not mint a new full entity
            $this dcterms:type ?type .
            FILTER(?type IN (
              era-edp-act:EDPUndo,
              era-edp-act:EDPAmend, era-edp-act:EDPCorrect,
              era-edp-act:EDPRequestRevoke, era-edp-act:EDPRevoke,
              era-edp-act:EDPWithdraw, era-edp-act:EDPSuspend, era-edp-act:EDPReject
            ))
          } UNION {
            # EDPSubmit following EDPRequestSubmit is validation — must not mint
            $this dcterms:type era-edp-act:EDPSubmit .
            BIND(era-edp-act:EDPSubmit AS ?type)
            $this prov:wasInformedBy ?reqAct .
            ?reqAct dcterms:type era-edp-act:EDPRequestSubmit .
          }
          $this prov:generated ?newEntity .
          FILTER(
            EXISTS { ?newEntity a era:CertificationLevelDocument }
            || EXISTS { ?newEntity a era:Certificate }
          )
        }
    """ .

era-sh:ActivityTimeOrderSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "prov:startedAtTime ({?start}) must be before or equal to prov:atTime ({?end})."@en ;
    sh:select """
        PREFIX prov: <http://www.w3.org/ns/prov#>

        SELECT $this ?start ?end WHERE {
          $this prov:startedAtTime ?start ;
                prov:atTime ?end .
          FILTER(?start > ?end)
        }
    """ .

era-sh:NonSubmitMustUseSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "This activity ({?type}) is not an EDPSubmit/EDPRequestSubmit and MUST declare at least one prov:used entity or snapshot."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>
        PREFIX prov: <http://www.w3.org/ns/prov#>
        PREFIX era-edp-act: <http://data.europa.eu/949/concepts/edp-activity-types/>

        SELECT $this ?type WHERE {
          $this dcterms:type ?type .
          FILTER(?type NOT IN (era-edp-act:EDPSubmit, era-edp-act:EDPRequestSubmit))
          FILTER NOT EXISTS { $this prov:used ?entity }
        }
    """ .

era-sh:UndoMustReferenceActivitySPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "This EDPUndo activity MUST reference the activity it reverses via prov:wasInformedBy."@en ;
    sh:select """
        PREFIX prov: <http://www.w3.org/ns/prov#>
        PREFIX dcterms: <http://purl.org/dc/terms/>
        PREFIX era-edp-act: <http://data.europa.eu/949/concepts/edp-activity-types/>

        SELECT $this WHERE {
          $this dcterms:type era-edp-act:EDPUndo .
          FILTER NOT EXISTS { $this prov:wasInformedBy ?prevAct }
        }
    """ .

era-sh:UndoTargetMustBeUndoableSPARQL
    a sh:SPARQLConstraint ;
    sh:severity sh:Violation ;
    sh:message "This EDPUndo targets activity {?prevAct} of type {?prevType}, but that type is not undoable. Only EDPRequestRevoke, EDPRequestSubmit, EDPRevoke, EDPWithdraw, EDPSuspend, EDPReject and EDPUndo can be undone."@en ;
    sh:select """
        PREFIX dcterms: <http://purl.org/dc/terms/>
        PREFIX prov: <http://www.w3.org/ns/prov#>
        PREFIX era-edp-act: <http://data.europa.eu/949/concepts/edp-activity-types/>

        SELECT $this ?prevAct ?prevType WHERE {
          $this dcterms:type era-edp-act:EDPUndo .
          $this prov:wasInformedBy ?prevAct .
          ?prevAct dcterms:type ?prevType .
          FILTER(?prevType NOT IN (
            era-edp-act:EDPRequestRevoke, era-edp-act:EDPRequestSubmit,
            era-edp-act:EDPRevoke, era-edp-act:EDPWithdraw,
            era-edp-act:EDPSuspend, era-edp-act:EDPReject, era-edp-act:EDPUndo
          ))
        }
    """ .

# NOTE: prov:qualifiedDerivation / prov:Derivation is NOT used.
#       The simple prov:wasDerivedFrom link is sufficient for
#       Group 2 activities. This keeps the model simpler and avoids
#       minting prov:Derivation resources explicitly.

#################################################################
#
#    4. Entity Snapshot shape (prov:Entity + specializationOf)
#
#    For Group 0/1 activities, changed property values are
#    preserved by creating lightweight prov:Entity snapshots.
#    A snapshot is NOT a new CLD/Certificate — it is an
#    "entity at a point in time" carrying ONLY the properties
#    that the activity changed (the delta).
#
#    Snapshots use prov:specializationOf to link back to the
#    main CLD/Certificate they describe. The PROV-O spec
#    states: "An entity that is a specialization of another
#    shares all aspects of the latter, and additionally
#    presents more specific aspects of the same thing."
#
#    This means a snapshot IS the EDP, but fixed to a
#    specific time window. It is NOT a separate resource
#    requiring all shape-mandated properties.
#
#    URI pattern: <mainEDP>/snapshot/<activity-local-id>
#
#################################################################


era-sh:EntitySnapshotShape
    a sh:NodeShape ;
    rdfs:label "Entity Snapshot (before/after)"@en ;
    rdfs:comment """Validates a lightweight prov:Entity snapshot that records the
    property values before or after a Group 0/1 (state-changing) activity.
    The snapshot carries only the delta — the properties that the
    activity actually changed. It is linked to the main CLD or
    Certificate via prov:specializationOf.

    A before-snapshot is prov:used by the activity (and
    prov:wasInvalidatedBy it). An after-snapshot is
    prov:generated by the activity."""@en ;

    # Target: any prov:Entity that has prov:specializationOf
    # (i.e. is a snapshot, not a standalone CLD/Certificate).
    # We cannot use sh:targetClass prov:Entity because that
    # would fire on every entity. Instead this shape is applied
    # via the SPARQL-based implicit target below.
    sh:target [
        a sh:SPARQLTarget ;
            sh:select """
            PREFIX prov: <http://www.w3.org/ns/prov#>
            SELECT ?this WHERE {
                ?this prov:specializationOf ?main .
            }
        """
    ] ;

    sh:property era-sh:EntitySnapshotSpecializationOf ;
    sh:property era-sh:EntitySnapshotGeneratedAtTime ;
    sh:property era-sh:EntitySnapshotInvalidatedAtTime ;

    # --- Ensure the snapshot is linked to an activity ---
    sh:sparql era-sh:EntitySnapshotActivityLink .

# --- prov:specializationOf — link to the main entity ---
era-sh:EntitySnapshotSpecializationOf
        a sh:PropertyShape ;
        sh:path prov:specializationOf  ;
        era:affectedProperty prov:specializationOf  ;
        sh:name "Specialization of main entity"@en ;
        sh:nodeKind sh:IRI ;
        sh:or (
            [ sh:class era:CertificationLevelDocument ]
            [ sh:class era:Certificate ]
        ) ;
        sh:description "The main CLD or Certificate that this snapshot describes at a specific point in time."@en ;
        sh:message "A snapshot MUST declare prov:specializationOf exactly one CLD or Certificate."@en ;
        sh:severity sh:Violation ;
        sh:minCount 1 ;
        sh:maxCount 1 .

# --- prov:generatedAtTime — when this snapshot became valid ---
era-sh:EntitySnapshotGeneratedAtTime
        a sh:PropertyShape ;
        sh:path prov:generatedAtTime  ;
        era:affectedProperty prov:generatedAtTime  ;
        sh:name "Snapshot valid from"@en ;
        sh:datatype xsd:dateTime ;
        sh:nodeKind sh:Literal ;
        sh:description "The point in time at which this snapshot became the active state of the entity."@en ;
        sh:message "A snapshot SHOULD have a prov:generatedAtTime."@en ;
        sh:severity sh:Warning ;
        sh:maxCount 1 .

# --- prov:invalidatedAtTime — when this snapshot was superseded ---
era-sh:EntitySnapshotInvalidatedAtTime
        a sh:PropertyShape ;
        sh:path prov:invalidatedAtTime  ;
        era:affectedProperty prov:invalidatedAtTime  ;
        sh:name "Snapshot valid until"@en ;
        sh:datatype xsd:dateTime ;
        sh:nodeKind sh:Literal ;
        sh:description "The point in time at which this snapshot was superseded (by the activity)."@en ;
        sh:message "A before-snapshot SHOULD have a prov:invalidatedAtTime."@en ;
        sh:severity sh:Info ;
        sh:maxCount 1 .

era-sh:EntitySnapshotActivityLink
        a sh:SPARQLConstraint ;
        sh:severity sh:Warning ;
        sh:message "This snapshot is not linked to any prov:Activity via prov:wasInvalidatedBy or prov:wasGeneratedBy."@en ;
            sh:select """
            PREFIX prov: <http://www.w3.org/ns/prov#>

            SELECT $this WHERE {
                $this prov:specializationOf ?main .
                FILTER NOT EXISTS { $this prov:wasInvalidatedBy ?act }
                FILTER NOT EXISTS { $this prov:wasGeneratedBy ?act2 }
            }
        """
.

#################################################################
#
#    5. Entity consistency shapes (reusable PropertyShapes)
#
#    Validates that the CLD / Certificate carries the
#    era:changeActivity link and that era:state is consistent
#    with any lifecycle activities.
#    These PropertyShapes are referenced from CertificationLevelDocumentShape and can
#    equally be referenced from a CertificateShape.
#
#################################################################


era-sh:EDPChangeActivityPropertyShape
    a sh:PropertyShape ;
    sh:path era:changeActivity  ;
    era:affectedProperty era:changeActivity  ;
    sh:name "Lifecycle activities"@en ;
    sh:nodeKind sh:IRI ;
    sh:class era:Activity ;
    sh:description "Lifecycle activities (amend, correct, revoke, withdraw, replace, etc.) recorded against this EDP via era:changeActivity."@en ;
    sh:message "(Info) No lifecycle activities recorded for this entity."@en ;
    sh:severity sh:Info .

era-sh:EDPStatePropertyShape
    a sh:PropertyShape ;
    sh:path era:state  ;
    era:affectedProperty era:state  ;
    sh:name "Entity state"@en ;
    sh:nodeKind sh:IRI ;
    sh:class skos:Concept ;
    era:inSkosConceptScheme <http://data.europa.eu/949/concepts/states/States> ;
    sh:description "The current state of the EDP (e.g. New, Amended, Revoked, Withdrawn, Suspended, Rejected)."@en ;
    sh:message "The entity SHOULD have an era:state from the States concept scheme."@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 ;
    sh:maxCount 1 .

# ============================================================================
# ERADIS ECM Certificate Shapes (SHACL 1.1 compatible)
# ============================================================================
# This file defines SHACL shapes for validating ECM Certificates
# as defined in Commission Implementing Regulation (EU) 2018/763.
#
# An ECM Certificate validates that an Entity in Charge of Maintenance
# complies with regulatory requirements.
#
# Key validation areas:
# - Certificate identification and type
# - ECM role and authorization
# - ECM Certification Body role and authorization
# - Validity period (start and end dates)
# - Type and extent of maintenance operations
# - Previous ECM Certificates (renewals/updates)
#
# Note: This is a SHACL 1.1 version. The SHACL 1.2 version with
# sh:targetWhere is in the SHACL 1.2/ subfolder.
# In SHACL 1.1, we use sh:SPARQLTarget (SHACL-AF) to target only
# era:Certificate instances with dcterms:type era-permission-types:ECMCertificate.
# ============================================================================


# ----------------------------------------------------------------------------
# Main Certificate Shape
# ----------------------------------------------------------------------------
# Uses sh:SPARQLTarget (SHACL-AF) to target only era:Certificate instances
# that have dcterms:type era-permission-types:ECMCertificate.

era-sh:ECMCertificateShape 
    a sh:NodeShape ;
    sh:target [
        a sh:SPARQLTarget ;
        sh:select """
            SELECT ?this WHERE {
                ?this a <http://data.europa.eu/949/Certificate> ;
                      <http://purl.org/dc/terms/type> <http://data.europa.eu/949/concepts/permission-types/ECMCertificate> .
            }
        """
    ] ;
    dcterms:relation era-permission-types:ECMCertificate ;

    rdfs:label "ECM Certificate" ;
    rdfs:comment "CERTIFICATE OF CONFORMITY OF ENTITY IN CHARGE OF MAINTENANCE - ECM Certificate"@en ;

    # ----------- Shape document management
    dcterms:creator [ 
      foaf:name "Maarten Duhoux" ;
      dcterms:source <https://orcid.org/0009-0003-6653-6123> ;
      org:memberOf <http://publications.europa.eu/resource/authority/corporate-body/ERA>
    ] ;
    rdfs:seeAlso "https://eradis.era.europa.eu/safety_docs/ecm/certificates/submit.aspx?DocType=1"^^xsd:anyURI ;
    dcterms:created "2025-05-22"^^xsd:date ;
    dcterms:modified "2025-05-23"^^xsd:date , "2025-06-19"^^xsd:date , "2025-10-24"^^xsd:date .

# ----------------------------------------------------------------------------
# 1. Certificate Type and Identification
# ----------------------------------------------------------------------------

# The dcterms:type of these Certificates must be ECMCertificate
era-sh:ECMCertificateShape sh:property era-sh:ECMTypeShape .
era-sh:ECMTypeShape a sh:PropertyShape ;
    sh:path dcterms:type  ;
    era:affectedProperty dcterms:type  ;
    sh:name "Certificate Type" ;
    sh:nodeKind sh:IRI ;
    sh:hasValue era-permission-types:ECMCertificate ;
    sh:maxCount 1 ;
    sh:minCount 1 ;
    sh:description "This Certificate must be typed as ECM Certificate" ;
    sh:message "An ECM Certificate MUST have `dcterms:type era-permission-types:ECMCertificate`"@en ;
    sh:severity sh:Violation .

# The dcterms:identifier follows the EU Identification Number format
era-sh:ECMCertificateShape sh:property era-sh:ECMIdentifierShape .
era-sh:ECMIdentifierShape a sh:PropertyShape ;
    sh:path dcterms:identifier  ;
    era:affectedProperty dcterms:identifier  ;
    sh:name "1 EU Identification Number (EIN)"@en ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:minCount 1 ; 
    sh:maxCount 1 ;
    sh:pattern "^[A-Z]{2}/31/[2-9][0-9]{3}/[0-9]{4}$" ;
    sh:description "Format: CC/31/YYYY/NNNN where CC=country code, 31=certificate type, YYYY=year, NNNN=sequential number" ;
    sh:message "An ECM Certificate MUST have ONE valid identifier matching pattern CC/31/YYYY/NNNN"@en ;
    sh:severity sh:Violation .

# ----------------------------------------------------------------------------
# 2. Entity in Charge of Maintenance (Certificate Holder)
# ----------------------------------------------------------------------------
# The certificate must be issued to an Entity in Charge of Maintenance with appropriate role classification.

era-sh:ECMCertificateShape sh:property era-sh:ECMRURoleShape .
era-sh:ECMRURoleShape a sh:PropertyShape ;
    sh:path dcterms:audience  ;
    era:affectedProperty dcterms:audience  ;
    sh:name "2. Certified Entity in Charge of Maintenance (ECM)"@en , 
            "2. Zertifizierte Instandhaltungsstelle (ECM)"@de ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:node era-sh:ECMRoleShape ;
    sh:class era:OrganisationRole ;
    sh:description "Links to the OrganisationRole defining the Entity in Charge of Maintenance"@en ;
    sh:message "An ECM Certificate MUST certify an organisation in a certain ECM role"@en ;
    sh:severity sh:Violation .

# Validates that the organisation role is an ECM
era-sh:ECMRoleShape a sh:NodeShape ;
    sh:nodeKind sh:IRI ;
    sh:property era-sh:ECMRoleOfShape ;
    sh:property era-sh:ECMHasRoleShape ;
    sh:description "Defines the role classification for the ECM" .

era-sh:ECMRoleOfShape a sh:PropertyShape ;
    sh:path era:roleOf  ;
    era:affectedProperty era:roleOf  ;
    sh:name "`era:Body` which is the ECM"@en ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:message "The organisation receiving an ECM Certificate must be an `era:Body`."@en ;
    sh:nodeKind sh:IRI ;
    sh:severity sh:Violation ;
    sh:class era:Body ;
    sh:description "Specifies the `era:Body` executing the role of ECM"@en .

era-sh:ECMHasRoleShape a sh:PropertyShape ;
    sh:path era:hasOrganisationRole  ;
    era:affectedProperty era:hasOrganisationRole  ;
    sh:name "Type of Company" ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:message "The OrganisationRole of an ECM Certificate holder must be selected/confirmed"@en ;
    sh:nodeKind sh:IRI ;
    sh:severity sh:Violation ;
    sh:class skos:Concept ;
    sh:in (
            era-organisation-roles:IM-1
            era-organisation-roles:IM-2
            era-organisation-roles:RU-1
            era-organisation-roles:RU-2
            era-organisation-roles:RU-3
            era-organisation-roles:RU-4
            era-organisation-roles:RU-5
            era-organisation-roles:Keeper
            era-organisation-roles:MS
            era-organisation-roles:SP
            era-organisation-roles:Manufacturer
    ) ;
    sh:description "Specifies the type of company which holds the ECM certificate" .


# ----------------------------------------------------------------------------
# 3. Certification Body
# ----------------------------------------------------------------------------
# The certificate must be issued by a Certification Body (ECM CB) with appropriate role classification.

era-sh:ECMCertificateShape sh:property era-sh:ECM-SCB-Shape .
era-sh:ECM-SCB-Shape a sh:PropertyShape ;
    sh:path dcterms:creator  ;
    era:affectedProperty dcterms:creator  ;
    sh:name "3. Certification Body" ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ; 
    sh:maxCount 1 ;
    sh:class era:OrganisationRole ;
    sh:node era-sh:ECM-CB-OrgRoleShape;
    sh:description "Links to the OrganisationRole defining the Certificate Issuing Organisation"@en ;
    sh:message "An ECM Certificate MUST link to the OrganisationRole Certificate Issuing Organisation"@en ;
    sh:severity sh:Violation .

# Validates that the organisation role is an ECM Certification Body
era-sh:ECM-CB-OrgRoleShape a sh:NodeShape ;
    sh:nodeKind sh:IRI ;
    sh:property era-sh:ECM-CB-RoleShape-ECM ;
    sh:property era-sh:ECM-CB-RoleOfShape-ECM ;
    sh:description "Defines the role classification for ECM Certification Bodies" .

era-sh:ECM-CB-RoleShape-ECM a sh:PropertyShape ;
    sh:path era:hasOrganisationRole  ;
    era:affectedProperty era:hasOrganisationRole  ;
    sh:name "ECM Certification Body Role Type" ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:message "An ECM Certificate must be issued by a era:Body having the role of ECM Certification Body"@en ;
    sh:nodeKind sh:IRI ;
    sh:severity sh:Violation ;
    sh:class skos:Concept ;
    sh:in ( era-organisation-roles:ECM-CB era-organisation-roles:NSA-ECM-CB ) ;
    sh:description "Specifies ECM Certification Body role" .

era-sh:ECM-CB-RoleOfShape-ECM a sh:PropertyShape ;
    sh:path era:roleOf  ;
    era:affectedProperty era:roleOf  ;
    sh:name "`era:Body` which is the ECM Certification Body (recognised, accredited, or a NSA)"@en ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:message "The organisation delivering an ECM Certificate must be an `era:Body`."@en ;
    sh:nodeKind sh:IRI ;
    sh:severity sh:Violation ;
    sh:class era:Body ;
    sh:description "Specifies the `era:Body` delivering the ECM certificate"@en .


# ----------------------------------------------------------------------------
# 4.2 EIN number of previous ECM certificate
# ----------------------------------------------------------------------------
# Optional reference to a previous certificate (renewal or update)

era-sh:ECMCertificateShape sh:property era-sh:ECMPreviousCertificateShape .
era-sh:ECMPreviousCertificateShape a sh:PropertyShape ;
    sh:path era:relatedCertificate  ;
    era:affectedProperty era:relatedCertificate  ;
    sh:name "EIN of the previous certificate"@en ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:minCount 0 ; sh:maxCount 1 ;
    sh:pattern "^[A-Z]{2}/31/[2-9][0-9]{3}/[0-9]{4}$" ;
    sh:description "Reference to a previous certificate with format CC/31/YYYY/NNNN" ;
    sh:message "An ECM Certificate MAY refer to ONE previous certificate identifier"@en ;
    sh:severity sh:Info .

# ----------------------------------------------------------------------------
# 4.3 Validity Period
# ----------------------------------------------------------------------------
# ECM Certificates are valid for up to 5 years.

era-sh:ECMCertificateShape sh:property era-sh:ECMValidityPeriodShape .
era-sh:ECMValidityPeriodShape a sh:PropertyShape ;
    sh:path dcterms:valid  ;
    era:affectedProperty dcterms:valid  ;
    sh:name "1.4 Validity Period" ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:qualifiedValueShape era-sh:StartEndValidityShape ;
    sh:qualifiedMinCount 1 ; sh:qualifiedMaxCount 1 ;
    sh:description "Defines the time interval during which the certificate is valid (max 5 years)" ;
    sh:message "An ECM Certificate MUST have ONE Validity Interval (start and end date)."@en ;
    sh:severity sh:Violation .

# ----------------------------------------------------------------------------
# 4.4 Permission type
# ----------------------------------------------------------------------------
# The role permitted by this ECM Certificate

era-sh:ECMCertificateShape sh:property era-sh:ECMPermissionShape .
era-sh:ECMPermissionShape a sh:PropertyShape ;
    sh:path vpa:permits  ;
    era:affectedProperty vpa:permits  ;
    sh:name "Permission type"@en ;
    sh:description "The role permitted by this ECM Certificate"@en ;
    sh:class era:OrganisationRole ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:in (
        era-organisation-roles:ECM
        era-organisation-roles:ECM-a
        era-organisation-roles:ECM-b
        era-organisation-roles:ECM-c
        era-organisation-roles:ECM-d
    ) .


# ----------------------------------------------------------------------------
# 5. SCOPE OF ECM ACTIVITIES
# ----------------------------------------------------------------------------
# Defines the vehicle categories.

# Multiple selection of the era-vehicle-types:SubCategories and era-vehicle-types:Categories concepts
era-sh:ECMCertificateShape sh:property era-sh:ECM-VehicleTypesShape .
era-sh:ECM-VehicleTypesShape a sh:PropertyShape ;
    sh:name "Category of vehicles"@en ;
    sh:path dcterms:coverage  ;
    era:affectedProperty dcterms:coverage  ;
    sh:class skos:Concept ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ; sh:maxCount 15 ;
    sh:description "Indicates the vehicle (sub)categories the ECM is allowed to maintain"@en ;
    sh:message "An ECM certificate must refer to all the vehicle (sub)categories which can be maintained by the holder"@en ;
    sh:severity sh:Violation .

# High-speed operation authorization
era-sh:ECMCertificateShape sh:property era-sh:ECM-RU-HS-OperationShape .
era-sh:ECM-RU-HS-OperationShape a sh:PropertyShape ;
    sh:name "High-speed Vehicles" ;
    sh:path era:coversHighSpeed  ;
    era:affectedProperty era:coversHighSpeed  ;
    sh:datatype xsd:boolean ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:description "Indicates whether the ECM is authorized to maintain vehicles intended for high-speed operations."@en ;
    sh:message "An ECM Certificate MUST indicate whether the ECM is authorized to maintain vehicles intended for high-speed operations"@en ;
    sh:severity sh:Violation .

# Dangerous goods (RID) operation authorization
era-sh:ECMCertificateShape sh:property era-sh:ECM-RU-RID-OperationShape .
era-sh:ECM-RU-RID-OperationShape a sh:PropertyShape ;
    sh:name "Covers wagons specialised in transport of dangerous goods" ;
    sh:path era:coversDangerousGoods  ;
    era:affectedProperty era:coversDangerousGoods  ;
    sh:datatype xsd:boolean ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:description "Indicates whether the ECM is authorized to maintain wagons specialised in transport of dangerous goods"@en ;
    sh:message "An ECM Certificate MUST indicate whether the ECM is authorized to maintain wagons specialised in transport of dangerous goods"@en ;
    sh:severity sh:Violation .

# Limitation to a site of the ECM
era-sh:ECMCertificateShape sh:property era-sh:ECM-LocationShape .
era-sh:ECM-LocationShape    
    a sh:PropertyShape ;
    sh:path dcterms:spatial  ;
    era:affectedProperty dcterms:spatial  ;
    sh:name "ECM: spatial validity to a Site"@en ;
    sh:nodeKind sh:BlankNodeOrIRI ;
    sh:class org:Site ;
    sh:description "Assessed site(s), if relevant."@en ;
    sh:message "No assessed site(s) mentioned in the ECM entitlement."@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 .


# ----------------------------------------------------------------------------
# 6 Additional information
# ----------------------------------------------------------------------------
era-sh:ECMCertificateShape sh:property era-sh:ECMAdditionalCommentsShape .
era-sh:ECMAdditionalCommentsShape a sh:PropertyShape ;
    sh:path rdfs:comment  ;
    era:affectedProperty rdfs:comment  ;
    sh:name "6 Additional information" ;
    sh:nodeKind sh:Literal ;
    sh:minCount 0 ; sh:maxCount 1 ;
    sh:datatype xsd:string ;
    sh:description "Additional information related to the certificate."@en ;
    sh:message "An ECM Certificate allows ONE Additional comment"@en ;
    sh:severity sh:Violation .

# ----------------------------------------------------------------------------
# Issue date
# ----------------------------------------------------------------------------
era-sh:ECMCertificateShape sh:property era-sh:ECMIssueDateShape .
era-sh:ECMIssueDateShape a sh:PropertyShape ;
    sh:path dcterms:issued  ;
    era:affectedProperty dcterms:issued  ;
    sh:name "5. Issue date" ;
    sh:datatype xsd:date ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:nodeKind sh:Literal ;
    sh:description "Date on which the certificate is issued." ;
    sh:message "An ECM Certificate MUST have ONE Issue date."@en ;
    sh:severity sh:Violation .

# Internal identifier for administrative purposes
era-sh:ECMCertificateShape sh:property era-sh:ECMInternalIdentifierShape .
era-sh:ECMInternalIdentifierShape a sh:PropertyShape ;
    sh:path rdfs:label  ;
    era:affectedProperty rdfs:label  ;
    sh:name "Internal Identification Number" ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:minCount 1 ; 
    sh:maxCount 1 ;
    sh:description "Internal reference number used by the issuing NSA" ;
    sh:message "An ECM Certificate SHOULD have an internal identifier"@en ;
    sh:severity sh:Warning .

# ============================================================================
# ERADIS ECM Certification Body Entitlement Shapes
# ============================================================================
# This file defines SHACL shapes for validating the entitlements of ECM Certification
# Bodies as defined in Commission Implementing Regulation (EU) YYYY/NNNN.
#
# An ECM Certification Body Entitlement validates that an ECM Certification Body
# complies with regulatory requirements.
#
# Key validation areas:
# - 1 Recognition/Accreditation entilement identification
# - 2 ECM Certification Body role and authorization
# - 3 ACCREDITATION / RECOGNITION / MEMBER STATE BODY (OPEN POINT)
# - 4 Category (OPEN POINT)
# - 5 Scope of certification (operations and vehicle type (sub)categories)
# - 6 Applicable national legislation
# - 7 Validity period (start and end dates)
# - 8 Additional infromation (decision date)
# - 9 Attached files
# - 10 Previous Recognition/Accreditation (renewals/updates)
# ============================================================================



# ----------------------------------------------------------------------------
# Main Certificate Shape
# ----------------------------------------------------------------------------
# Targets all instances of era:Certificate which are of type.

era-sh:ECMCertBodyPermissionShape 
    a sh:NodeShape ;
    sh:targetClass era:Certificate ;
    sh:nodeKind sh:IRI ;
    dcterms:relation era-permission-types:ECMCertification ;

    rdfs:label "ECM Certification Body Attestation"@en , "Attestation"@fr ;
    rdfs:comment "Proof of Entitlement - ECM Certification Body"@en;

    # ----------- Shape document management
    dcterms:creator [ 
      foaf:name "Maarten Duhoux" ;
      dcterms:source <https://orcid.org/0009-0003-6653-6123> ;
      org:memberOf <http://publications.europa.eu/resource/authority/corporate-body/ERA>
    ] ;
    rdfs:seeAlso "https://eradis.era.europa.eu/safety_docs/ecm/certBodies/default.aspx"^^xsd:anyURI ;
    dcterms:created "2025-10-29"^^xsd:date .

# ----------------------------------------------------------------------------
# 1. Certificate Type and Identification
# ----------------------------------------------------------------------------

# The dcterms:type of these Certificates
era-sh:ECMCertBodyPermissionShape sh:property era-sh:ECMCBTypeShape .
era-sh:ECMCBTypeShape a sh:PropertyShape ;
    sh:path dcterms:type  ;
    era:affectedProperty dcterms:type  ;
    sh:name "Certificate Type" ;
    sh:nodeKind sh:IRI ;
    sh:hasValue era-permission-types:ECMCertification  ;
    sh:maxCount 1 ;
    sh:minCount 1 ;
    sh:description "This Attestation serves as Proof of Entitlement for an ECM Certification Body" ;
    sh:message "An ECM Attestation MUST have `dcterms:type era-permission-types:ECMCertification`"@en ;
    sh:severity sh:Violation .

# The dcterms:identifier follows the EU Identification Number format
era-sh:ECMCertBodyPermissionShape sh:property era-sh:ECMCBIdentifierShape .
era-sh:ECMCBIdentifierShape a sh:PropertyShape ;
    sh:path dcterms:identifier  ;
    era:affectedProperty dcterms:identifier  ;
    sh:name "1. ID DATA - EU Identification Number (EIN)"@en ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:minCount 1 ; 
    sh:maxCount 1 ;
    sh:pattern "^[A-Z]{2}/30/[0-9]{4}/[0-9]{4}$" ; # EIN of ECM Certification Body permission
    sh:description "Format: CC/30/MMYY/NNNN where CC=country code, 30=certificate type, YY=year, MM & NNNN=sequential number" ;
    sh:message "An ECM Attestation MUST have ONE valid identifier matching pattern CC/30/MMYY/NNNN"@en ;
    sh:severity sh:Violation .

# ----------------------------------------------------------------------------
# 2. Certification Body 
# ----------------------------------------------------------------------------
# The certificate must be issued to an ECM CB.

era-sh:ECMCertBodyPermissionShape sh:property era-sh:ECMCertBodyCertificationBodyShape .
era-sh:ECMCertBodyCertificationBodyShape a sh:PropertyShape ;
    sh:path dcterms:audience  ;
    era:affectedProperty dcterms:audience  ;
    sh:name "2. Certification Body" ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ; 
    sh:maxCount 1 ;
    sh:node era-sh:ECM-CB-OrgRoleShape ;
    sh:class era:OrganisationRole ;
    sh:description "Links to the OrganisationRole defining the Certificate Issuing Organisation"@en ;
    sh:message "The audience of the ECM CB proof of entitlement"@en ;
    sh:severity sh:Violation .

# Validates that the organisation role is a Railway Undertaking (RU)
era-sh:ECM-CB-OrgRoleShape a sh:NodeShape ;
    sh:nodeKind sh:IRI ;
    sh:property era-sh:ECM-CB-RoleShape-ECMCB ;
    sh:property era-sh:ECM-CB-RoleOfShape-ECMCB ;
    sh:description "Defines the role classification for ECM Certification Bodies" .

era-sh:ECM-CB-RoleShape-ECMCB a sh:PropertyShape ;
    sh:path era:hasOrganisationRole  ;
    era:affectedProperty era:hasOrganisationRole  ;
    sh:name "ECM Certification Body Role Type" ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:message "An ECM Certification Body attestation must be held by a era:Body having the role of ECM Certification Body"@en ;
    sh:nodeKind sh:IRI ;
    sh:severity sh:Violation ;
    sh:class skos:Concept ;
    sh:in ( era-organisation-roles:ECM-CB era-organisation-roles:NSA-ECM-CB ) ;
    sh:description "Specifies ECM Certification Body role" .

era-sh:ECM-CB-RoleOfShape-ECMCB a sh:PropertyShape ;
    sh:path era:roleOf  ;
    era:affectedProperty era:roleOf  ;
    sh:name "`era:Body` which is the ECM Certification Body (recognised, accredited, or a NSA)"@en ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:message "The organisation receiving an ECM CB attestation must be an `era:Body`."@en ;
    sh:nodeKind sh:IRI ;
    sh:severity sh:Violation ;
    sh:class era:Body ;
    sh:description "Specifies the `era:Body` receiving the ECM CB attestation"@en .

# ----------------------------------------------------------------------------
# 3. ACCREDITATION / RECOGNITION / MEMBER STATE BODY
# ----------------------------------------------------------------------------
# The certificate must be issued by a ACCREDITATION / RECOGNITION / MEMBER STATE BODY.

# FIXME: open point, given that a NSA cannot be ECM CB when at the same time executing the ECM CB accrediation role.


# ----------------------------------------------------------------------------
# 4 Category of ECM activities
# ----------------------------------------------------------------------------
# Category: Accredited, Recognised, NSA

# FIXME: should be deduced from 3


# ----------------------------------------------------------------------------
# 5. SCOPE OF Certification
# ----------------------------------------------------------------------------
# Defines the scope and the vehicle (sub)categories.

era-sh:ECMCertBodyPermissionShape sh:property era-sh:ECM-CB-MaintenanceFunctionsShape .
era-sh:ECM-CB-MaintenanceFunctionsShape a sh:PropertyShape ;
    sh:path dcterms:coverage  ;
    era:affectedProperty dcterms:coverage  ;
    sh:name "5.1a The maintenance functions which can be certified by the ECM CB"@en ;
    sh:class skos:Concept ;
    sh:in (
        era-mf:MDEV
        era-mf:MDEL
        era-mf:FMM
    );
    sh:minCount 0 ; sh:maxCount 3 ;
    sh:description "Indicates the certifiable Maintenance Functions"@en ;
    sh:message ""@en ;
    sh:severity sh:Violation .

era-sh:ECMCertBodyPermissionShape sh:property era-sh:ECM-CB-ECMTypesAllowedShape .
era-sh:ECM-CB-ECMTypesAllowedShape a sh:PropertyShape ;
    sh:path dcterms:coverage  ;
    era:affectedProperty dcterms:coverage  ;
    sh:name "5.1b The ECM scope which can be certified by the ECM CB"@en ;
    sh:class skos:Concept ;
    sh:in (
        era-organisation-roles:ECM      # General ECM
        era-organisation-roles:ECM-a   
        era-organisation-roles:ECM-b   
        era-organisation-roles:ECM-c   
        era-organisation-roles:ECM-d  
    );
    sh:minCount 0 ; sh:maxCount 5 ;
    sh:description "Indicates the certifiable ECM scopes"@en ;
    sh:message ""@en ;
    sh:severity sh:Violation .


# Multiple selection of the era-vehicle-types:SubCategories and era-vehicle-types:Categories concepts
era-sh:ECMCertBodyPermissionShape sh:property era-sh:ECM-CB-VTCategoriesShape .
era-sh:ECM-CB-VTCategoriesShape a sh:PropertyShape ;
    sh:name "5.2 Category of vehicles"@en ;
    sh:path dcterms:coverage  ;
    era:affectedProperty dcterms:coverage  ;
   # owl:imports <http://data.europa.eu/949/concepts/vehicle-types/eratv/Categories> ;
    sh:class skos:Concept ; # FIXME: Sparql validation on the SKOS concept schemes is still to be added
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ; sh:maxCount 15 ;
    sh:description "Indicates the vehicle categories the ECM CB is allowed to certify"@en ;
    sh:message "An ECM CB Attestation MUST indicate what vehicle categories the ECM CB is authorized to certify operations on."@en ;
    sh:severity sh:Violation .

era-sh:ECMCertBodyPermissionShape sh:property era-sh:ECM-CB-VTSubCategoriesShape .
era-sh:ECM-CB-VTSubCategoriesShape a sh:PropertyShape ;
    sh:name "5.3 Subcategory of vehicles"@en ;
    sh:path dcterms:coverage  ;
    era:affectedProperty dcterms:coverage  ;
   # owl:imports <http://data.europa.eu/949/concepts/vehicle-types/eratv/Subcategories> ;
    sh:class skos:Concept ; # FIXME: Sparql validation on the SKOS concept schemes is still to be added
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ; sh:maxCount 15 ;
    sh:description "Indicates the vehicle subcategories the ECM CB is allowed to certify"@en ;
    sh:message "An ECM CB Attestation MUST indicate what vehicle subcategories the ECM CB is authorized to certify operations on."@en ;
    sh:severity sh:Violation .

# High-speed operation authorization
era-sh:ECMCertBodyPermissionShape sh:property era-sh:ECMCB-HS-OperationShape .
era-sh:ECMCB-HS-OperationShape a sh:PropertyShape ;
    sh:name "5.4a Covers High-speed Vehicles" ;
    sh:path era:coversHighSpeed  ;
    era:affectedProperty era:coversHighSpeed  ;
    sh:datatype xsd:boolean ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:description "Indicates whether the ECM CB is authorized to certify operations regarding vehicles intended for high-speed operations."@en ;
    sh:message "An ECM CB Attestation MUST indicate whether the ECM CB is authorized to certify operations regarding vehicles intended for high-speed operations"@en ;
    sh:severity sh:Violation .

# Dangerous goods (RID) operation authorization
era-sh:ECMCertBodyPermissionShape sh:property era-sh:ECMCB-RID-OperationShape .
era-sh:ECMCB-RID-OperationShape a sh:PropertyShape ;
    sh:name "5.4b Covers wagons specialised in transport of dangerous goods" ;
    sh:path era:coversDangerousGoods  ;
    era:affectedProperty era:coversDangerousGoods  ;
    sh:datatype xsd:boolean ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:description "Indicates whether the ECM CB is authorized to certify operations regarding wagons specialised in transport of dangerous goods"@en ;
    sh:message "An ECM CB Attestation MUST indicate whether the ECM CB is authorized to certify operations regarding wagons specialised in transport of dangerous goods"@en ;
    sh:severity sh:Violation .

# ----------------------------------------------------------------------------
# 6 Applicable national legislation
# ----------------------------------------------------------------------------
# National Acts under which the certificate is issued.
era-sh:ECMCertBodyPermissionShape sh:property era-sh:ECMCertBodyApplicableNationalLegislationShape .
era-sh:ECMCertBodyApplicableNationalLegislationShape a sh:PropertyShape ;
    sh:path dcterms:source  ;
    era:affectedProperty dcterms:source  ;
    sh:name "6 Applicable National Legislation" ;
    sh:nodeKind sh:Literal ;
    sh:minCount 1 ; sh:maxCount 5 ;
    sh:datatype xsd:string ;
    # dash:singleLine is from DASH Data Shapes vocabulary (not SHACL Core) - used for UI hints
    # dash:singleLine false ;
    sh:description "The national legislation under which the attestation is issued."@en ;
    sh:message "An ECM CB Attestation MUST refer to at least ONE applicable national legislation"@en ;
    sh:severity sh:Violation .


# ----------------------------------------------------------------------------
# 7 Validity Period
# ----------------------------------------------------------------------------
# ECM CB Attestations are valid for up to 5 years.

era-sh:ECMCertBodyPermissionShape sh:property era-sh:ECMValidityShape .
era-sh:ECMValidityShape a sh:PropertyShape ;
    sh:path dcterms:valid  ;
    era:affectedProperty dcterms:valid  ;
    sh:name "7 Validity Period" ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:qualifiedValueShape era-sh:StartEndValidityShape ;
    sh:qualifiedMinCount 1 ; sh:qualifiedMaxCount 1 ;
    sh:description "Defines the time interval during which the attestation is valid (max 5 years)" ;
    sh:message "An ECM CB Attestation MUST have ONE Validity Interval (start and end date)."@en ;
    sh:severity sh:Violation .

# ----------------------------------------------------------------------------
# 8. ADDITIONAL INFORMATION
# ----------------------------------------------------------------------------
# 

era-sh:ECMCertBodyPermissionShape sh:property era-sh:ECMDecisionDateShape .
era-sh:ECMDecisionDateShape a sh:PropertyShape ;
    sh:path dcterms:issued  ;
    era:affectedProperty dcterms:issued  ;
    sh:name "Date of decision" ;
    sh:datatype xsd:date ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:nodeKind sh:Literal ;
    sh:description "Date on which the certificate is issued." ;
    sh:message "An ECM CB Attestation MUST have ONE Issue date (decision date)."@en ;
    sh:severity sh:Violation .

# Internal identifier for administrative purposes
era-sh:ECMCertBodyPermissionShape sh:property era-sh:ECMCertBodyInternalIdentifierShape .
era-sh:ECMCertBodyInternalIdentifierShape a sh:PropertyShape ;
    sh:path rdfs:label  ;
    era:affectedProperty rdfs:label  ;
    sh:name "Internal Identification Number" ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:minCount 1 ; 
    sh:maxCount 1 ;
    sh:description "Internal reference number used by the issuing NSA" ;
    sh:message "An ECM CB Attestation SHOULD have an internal identifier"@en ;
    sh:severity sh:Warning .

# ----------------------------------------------------------------------------
# 10 EIN number of previous ECM certificate
# ----------------------------------------------------------------------------
# Optional reference to a previous certificate (renewal or update)

era-sh:ECMCertBodyPermissionShape sh:property era-sh:ECMCertBodyPreviousCertificateShape .
era-sh:ECMCertBodyPreviousCertificateShape a sh:PropertyShape ;
    sh:path era:relatedCertificate  ;
    era:affectedProperty era:relatedCertificate  ;
    sh:name "EIN of previous version"@en ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:minCount 0 ; sh:maxCount 1 ;
    sh:pattern "^[A-Z]{2}/30/[0-9]{4}/[0-9]{4}$" ;
    sh:description "Reference to a previous certificate with format CC/30/MMMM/NNNN" ;
    sh:message "An ECM CD entitlement can refer to a previous version"@en ;
    sh:severity sh:Warning .

# ============================================================================
# ERADIS Certificate of conformity for Maintenance Functions Shapes
# ============================================================================
# This file defines SHACL shapes for validating CERTIFICATES OF CONFORMITY FOR MAINTENANCE FUNCTIONS
# as defined in Commission Implementing Regulation (EU) 2019/779.
#
# A `Certificate of conformity for Maintenance Functions` validates that an Entitiy in Charge of Maintenance
# complies with regulatory requirements.
#
# Key validation areas:
# - Certificate identification and type
# - Role and authorisation of the Certified Organisation
# - Certification Body role and authorization
# - Validity period (start and end dates)
# - Type and extent of maintenance functions
# - Previous Certificate of conformity for Maintenance Functions (renewals/updates)
# ============================================================================



# ----------------------------------------------------------------------------
# Main Certificate Shape
# ----------------------------------------------------------------------------
# Targets all instances of era:Certificate which cover CONFORMITY FOR MAINTENANCE FUNCTIONS.

era-sh:MWSCertificateShape 
    a sh:NodeShape ;
    sh:targetClass era:Certificate ;
    sh:nodeKind sh:IRI ;
    dcterms:relation era-permission-types:MaintenanceFunctionCertificate ; 

    rdfs:label "Certificate of conformity for Maintenance Functions"@en ;
    rdfs:comment "CERTIFICATE OF CONFORMITY OF Certified Organisation - ECM Certificate"@en;

    rdfs:seeAlso "https://eradis.era.europa.eu/safety_docs/ecm/certificates/submit.aspx?DocType=3"^^xsd:anyURI ;
    dcterms:created "2025-06-19"^^xsd:date ;
    dcterms:modified "2025-07-23"^^xsd:date , "2025-10-24"^^xsd:date ;
    .

# ----------------------------------------------------------------------------
# 1. Certificate Type and Identification
# ----------------------------------------------------------------------------

# The dcterms:type of these Certificates must be SingleSafetyCertificate
era-sh:MWSCertificateShape sh:property era-sh:MWSTypeShape .
era-sh:MWSTypeShape a sh:PropertyShape ;
    sh:path dcterms:type  ;
    era:affectedProperty dcterms:type  ;
    sh:name "Certificate Type" ;
    sh:nodeKind sh:IRI ;
    sh:hasValue era-permission-types:MaintenanceFunctionCertificate ;
    sh:maxCount 1 ; sh:minCount 1 ;
    sh:description "This Certificate must be typed as `Certificate of conformity for Maintenance Functions`" ;
    sh:message "A `Certificate of conformity for Maintenance Functions` MUST have `dcterms:type era-permission-types:MaintenanceFunctionCertificate`"@en ;
    sh:severity sh:Violation .

# The dcterms:identifier follows the EU Identification Number format
era-sh:MWSCertificateShape sh:property era-sh:MWSIdentifierShape .
era-sh:MWSIdentifierShape a sh:PropertyShape ;
    sh:path dcterms:identifier  ;
    era:affectedProperty dcterms:identifier  ;
    sh:name "1 EU Identification Number (EIN)"@en ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:minCount 1 ; 
    sh:maxCount 1 ;
    sh:pattern "^[A-Z]{2}/3[2-3]/[2-9][0-9]{3}/[0-9]{4}$" ; # EIN of Certificate of conformity for Maintenance Functions
    sh:description "Format: CC/32|33/YYYY/NNNN where CC=country code, 32|33=certificate type, YYYY=year, NNNN=sequential number" ;
    sh:message "A `Certificate of conformity for Maintenance Functions` MUST have ONE valid identifier matching pattern CC/32/YYYY/NNNN"@en ;
    sh:severity sh:Violation .

# ----------------------------------------------------------------------------
# 2. Organisation (Certificate Holder)
# ----------------------------------------------------------------------------
# The certificate must be issued to a Organisation with appropriate role classification.

era-sh:MWSCertificateShape sh:property era-sh:MWSRURoleShape .
era-sh:MWSRURoleShape a sh:PropertyShape ;
    sh:path dcterms:audience  ;
    era:affectedProperty dcterms:audience  ;
    sh:name "2. Certified Organisation"@en , 
            "2. ZERTIFIZIERTE ORGANISATION"@de ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:node era-sh:MWSRoleShape ;
    sh:class era:OrganisationRole ;
    sh:description "Links to the OrganisationRole defining the Certified Organisation"@en ;
    sh:message "A `Certificate of conformity for Maintenance Functions` MUST certify an organisation in a certain role"@en ;
    sh:severity sh:Violation .

# Validates that the organisation role is a ECM
era-sh:MWSRoleShape a sh:NodeShape ;
    sh:nodeKind sh:IRI ;
    sh:property era-sh:MWSRoleOfShape ;
    sh:property era-sh:MWSHasRoleShape ;
    sh:description "Defines the role classification for the ECM" .

era-sh:MWSRoleOfShape a sh:PropertyShape ;
    sh:path era:roleOf  ;
    era:affectedProperty era:roleOf  ;
    sh:name "`era:Body` which executes the Maintenance Functions"@en ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:message "The organisation receiving a `Certificate of conformity for Maintenance Functions` must be an `era:Body`."@en ;
    sh:nodeKind sh:IRI ;
    sh:severity sh:Violation ;
    sh:class era:Body ;
    sh:description "Specifies the `era:Body` executing the Maintenance Functions"@en .

era-sh:MWSHasRoleShape a sh:PropertyShape ;
    sh:path era:hasOrganisationRole  ;
    era:affectedProperty era:hasOrganisationRole  ;
    sh:name "Type of Company" ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:message "The OrganisationRole of a `Certificate of conformity for Maintenance Functions` holder must be selected/confirmed"@en ;
    sh:nodeKind sh:IRI ;
    sh:severity sh:Violation ;
    sh:class skos:Concept ;
    sh:in (
            era-organisation-roles:IM-1
            era-organisation-roles:IM-2
            era-organisation-roles:RU-1
            era-organisation-roles:RU-2
            era-organisation-roles:RU-3
            era-organisation-roles:RU-4
            era-organisation-roles:RU-5
            era-organisation-roles:Keeper
            era-organisation-roles:MS
            era-organisation-roles:SP
            era-organisation-roles:Manufacturer
    );
    sh:description "Specifies the type of company which holds the Certificate of conformity for Maintenance Functions" .


# ----------------------------------------------------------------------------
# 3. Certification Body
# ----------------------------------------------------------------------------
# The certificate must be issued by a Certification Body (ECM CB) with appropriate role classification.

era-sh:MWSCertificateShape sh:property era-sh:MWS-SCB-Shape .
era-sh:MWS-SCB-Shape a sh:PropertyShape ;
    sh:path dcterms:creator  ;
    era:affectedProperty dcterms:creator  ;
    sh:name "3. Certification Body" ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ; 
    sh:maxCount 1 ;
    sh:node era-sh:MWS-CB-OrgRoleShape ;
    sh:class era:OrganisationRole ;
    sh:description "Links to the OrganisationRole defining the Certificate Issuing Organisation"@en ;
    sh:message "A `Certificate of conformity for Maintenance Functions` MUST link to the OrganisationRole Certificate Issuing Organisation"@en ;
    sh:severity sh:Violation .

# Validates that the organisation role is a Railway Undertaking (RU)
era-sh:MWS-CB-OrgRoleShape a sh:NodeShape ;
    sh:nodeKind sh:IRI ;
    sh:property era-sh:MWS-CB-RoleShape ;
    sh:property era-sh:MWS-CB-RoleOfShape ;
    sh:description "Defines the role classification for ECM Certification Bodies" .

era-sh:MWS-CB-RoleShape a sh:PropertyShape ;
    sh:path era:hasOrganisationRole  ;
    era:affectedProperty era:hasOrganisationRole  ;
    sh:name "ECM Certification Body Role Type" ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:message "A `Certificate of conformity for Maintenance Functions` must be issued by a era:Body having the role of ECM Certification Body"@en ;
    sh:nodeKind sh:IRI ;
    sh:severity sh:Violation ;
    sh:class skos:Concept ;
    sh:in ( era-organisation-roles:ECM-CB era-organisation-roles:NSA-ECM-CB ) ;
    sh:description "Specifies ECM Certification Body role" .

era-sh:MWS-CB-RoleOfShape a sh:PropertyShape ;
    sh:path era:roleOf  ;
    era:affectedProperty era:roleOf  ;
    sh:name "`era:Body` which is the ECM Certification Body (recognised, accredited, or a NSA)"@en ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:message "The organisation delivering a `Certificate of conformity for Maintenance Functions` must be an `era:Body`."@en ;
    sh:nodeKind sh:IRI ;
    sh:severity sh:Violation ;
    sh:class era:Body ;
    sh:description "Specifies the `era:Body` delivering the Certificate of conformity for Maintenance Functions"@en .


# ----------------------------------------------------------------------------
# 4.2 EIN number of previous Certificate of conformity for Maintenance Functions
# ----------------------------------------------------------------------------
# Optional reference to a previous certificate (renewal or update)

era-sh:MWSCertificateShape sh:property era-sh:MWSPreviousCertificateShape .
era-sh:MWSPreviousCertificateShape a sh:PropertyShape ;
    sh:path era:relatedCertificate  ;
    era:affectedProperty era:relatedCertificate  ;
    sh:name "EIN of the previous certificate"@en ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:minCount 0 ; sh:maxCount 1 ;
    sh:pattern "^[A-Z]{2}/3[2-3]/[2-9][0-9]{3}/[0-9]{4}$" ;
    sh:description "Reference to a previous certificate with format CC/32/YYYY/NNNN or CC/33/YYYY/NNNN" ;
    sh:message "A `Certificate of conformity for Maintenance Functions` MAY refer to ONE previous certificate identifier"@en ;
    sh:severity sh:Info .

# ----------------------------------------------------------------------------
# 4.3 Validity Period
# ----------------------------------------------------------------------------
# Certificate of conformity for Maintenance Functionss are valid for up to 5 years.

era-sh:MWSCertificateShape sh:property era-sh:MWSValidityShape .
era-sh:MWSValidityShape a sh:PropertyShape ;
    sh:path dcterms:valid  ;
    era:affectedProperty dcterms:valid  ;
    sh:name "4.3 Validity Period" ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:qualifiedValueShape era-sh:StartEndValidityShape ;
    sh:qualifiedMinCount 1 ; sh:qualifiedMaxCount 1 ;
    sh:description "Defines the time interval during which the certificate is valid (max 5 years)" ;
    sh:message "A `Certificate of conformity for Maintenance Functions` MUST have ONE Validity Interval (start and end date)."@en ;
    sh:severity sh:Violation .

# ----------------------------------------------------------------------------
# 4.4 Permission type
# ----------------------------------------------------------------------------
# The role permitted by this Certificate of conformity for Maintenance Functions

era-sh:MWSCertificateShape sh:property era-sh:MWSPermissionShape .
era-sh:MWSPermissionShape a sh:PropertyShape ;
    sh:path vpa:permits  ;
    era:affectedProperty vpa:permits  ;
    sh:name "Permission type"@en ;
    sh:description "The role permitted by this `Certificate of conformity for Maintenance Functions`"@en ;
    sh:class era:OrganisationRole ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:or (
        [ sh:hasValue era-organisation-roles:MWS ]     # General MWS
        [ sh:hasValue era-organisation-roles:MWS-a ]   
        [ sh:hasValue era-organisation-roles:MWS-b ]   
        [ sh:hasValue era-organisation-roles:MWS-c ]   
        [ sh:hasValue era-organisation-roles:MWS-d ]   
    ) ;
.


# ----------------------------------------------------------------------------
# 5. SCOPE OF ECM ACTIVITIES / UMFANG DER ECM TÄTIGKEITEN
# ----------------------------------------------------------------------------
# Defines the vehicle categories.

# Multiple selection of the era-vehicle-types:SubCategories and era-vehicle-types:Categories concepts
era-sh:MWSCertificateShape sh:property era-sh:MWS-VehicleTypesShape .
era-sh:MWS-VehicleTypesShape a sh:PropertyShape ;
    sh:name "Category of vehicles"@en ;
    sh:path dcterms:coverage  ;
    era:affectedProperty dcterms:coverage  ;
    owl:imports <http://data.europa.eu/949/concepts/vehicle-types/eratv/Categories> , <http://data.europa.eu/949/concepts/vehicle-types/eratv/Subcategories> ;
    sh:class skos:Concept ; # FIXME: Sparql validation on the SKOS concept schemes is still to be added
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ; sh:maxCount 15 ;
    sh:description "Indicates the vehicle (sub)categories the ECM is allowed to maintain"@en ;
    sh:message "A `Certificate of conformity for Maintenance Functions` must refer to all the vehicle (sub)categories which can be maintained by the holder"@en ;
    sh:severity sh:Violation .

# High-speed operation authorization
era-sh:MWSCertificateShape sh:property era-sh:MWS-RU-HS-OperationShape .
era-sh:MWS-RU-HS-OperationShape a sh:PropertyShape ;
    sh:name "High-speed Vehicles" ;
    sh:path era:coversHighSpeed  ;
    era:affectedProperty era:coversHighSpeed  ;
    sh:datatype xsd:boolean ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:description "Indicates whether the ECM is authorized to maintain vehicles intended for high-speed operations."@en ;
    sh:message "A `Certificate of conformity for Maintenance Functions` MUST indicate whether the ECM is authorized to maintain vehicles intended for high-speed operations"@en ;
    sh:severity sh:Violation .

# Dangerous goods (RID) operation authorization
era-sh:MWSCertificateShape sh:property era-sh:MWS-RU-RID-OperationShape .
era-sh:MWS-RU-RID-OperationShape a sh:PropertyShape ;
    sh:name "Covers wagons specialised in transport of dangerous goods" ;
    sh:path era:coversDangerousGoods  ;
    era:affectedProperty era:coversDangerousGoods  ;
    sh:datatype xsd:boolean ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:description "Indicates whether the ECM is authorized to maintain wagons specialised in transport of dangerous goods"@en ;
    sh:message "A `Certificate of conformity for Maintenance Functions` MUST indicate whether the ECM is authorized to maintain wagons specialised in transport of dangerous goods"@en ;
    sh:severity sh:Violation .

# Limitation to a site of the ECM
era-sh:MWSCertificateShape sh:property era-sh:MWS-LocationShape .
era-sh:MWS-LocationShape a sh:PropertyShape ;
    sh:path dcterms:spatial  ;
    era:affectedProperty dcterms:spatial  ;
    sh:name "MWS: spatial validity to a Site"@en ;
    sh:nodeKind sh:BlankNodeOrIRI ;
    sh:class org:Site ;
    sh:description "Assessed site(s), if relevant."@en ;
    sh:message "No assessed site(s) mentioned in the MWS entitlement."@en ;
    sh:severity sh:Warning ;
    sh:minCount 1 .


# ----------------------------------------------------------------------------
# 6 Maintenance functions
# ----------------------------------------------------------------------------
era-sh:MWSCertificateShape sh:property era-sh:MWS-FunctionsShape .
era-sh:MWS-FunctionsShape a sh:PropertyShape ;
    sh:path dcterms:coverage  ;
    era:affectedProperty dcterms:coverage  ;
    sh:name "The maintenance functions covered by the Certificate"@en ;
    sh:class skos:Concept ;
    sh:in (
        era-mf:MDEV
        era-mf:MDEL
        era-mf:FMM
    );
    sh:minCount 1 ; sh:maxCount 9 ;
    sh:description "Indicates the covered Maintenance Functions"@en ;
    sh:message "A `Certificate of conformity for Maintenance Functions` MUST indicate which maintenance functions are supported"@en ;
    sh:severity sh:Violation .



# ----------------------------------------------------------------------------
# 7 Additional information
# ----------------------------------------------------------------------------
# National Acts under which the certificate is issued.
era-sh:MWSCertificateShape sh:property era-sh:MWSAdditionalCommentsShape .
era-sh:MWSAdditionalCommentsShape a sh:PropertyShape ;
    sh:path rdfs:comment  ;
    era:affectedProperty rdfs:comment  ;
    sh:name "6 Additional information" ;
    sh:nodeKind sh:Literal ;
    sh:minCount 0 ; sh:maxCount 1 ;
    # dash:singleLine is from DASH Data Shapes vocabulary (not SHACL Core) - used for UI hints
    # dash:singleLine false ;
    sh:datatype xsd:string ;
    sh:description "Additional information related to the certificate."@en ;
    sh:message "A `Certificate of conformity for Maintenance Functions` allows ONE Additional comment"@en ;
    sh:severity sh:Violation .

# ----------------------------------------------------------------------------
# Issue date
# ----------------------------------------------------------------------------
# Certificate of conformity for Maintenance Functions issue date

era-sh:MWSCertificateShape sh:property era-sh:MWSIssueDateShape .
era-sh:MWSIssueDateShape a sh:PropertyShape ;
    sh:path dcterms:issued  ;
    era:affectedProperty dcterms:issued  ;
    sh:name "5. Issue date" ;
    sh:datatype xsd:date ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:nodeKind sh:Literal ;
    sh:description "Date on which the certificate is issued." ;
    sh:message "A `Certificate of conformity for Maintenance Functions` MUST have ONE Issue date."@en ;
    sh:severity sh:Violation .

# Internal identifier for administrative purposes
era-sh:MWSCertificateShape sh:property era-sh:MWSInternalIdentifierShape .
era-sh:MWSInternalIdentifierShape a sh:PropertyShape ;
    sh:path rdfs:label  ;
    era:affectedProperty rdfs:label  ;
    sh:name "Internal Identification Number" ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:minCount 1 ; 
    sh:maxCount 1 ;
    sh:description "Internal reference number used by the issuing NSA" ;
    sh:message "A `Certificate of conformity for Maintenance Functions` SHOULD have an internal identifier"@en ;
    sh:severity sh:Warning .

# ============================================================================
# ERADIS Safety Certificate Part A Shapes
# ============================================================================
# This file defines SHACL shapes for validating Safety Certificates Part A
# as defined in Commission Implementing Regulation (EU) 2018/763.
#
# A Safety Certificate Part A validates that a Railway Undertaking's (RU)
# Safety Management System (SMS) complies with regulatory requirements.
# It is issued by a National Safety Authority (NSA) and is valid for 5 years.
#
# Key validation areas:
# - Certificate identification and type
# - Railway Undertaking role and authorization
# - Validity period (start and end dates)
# - Type and extent of service (passenger/freight, volumes, operations)
# - Related ECM certificates
# ============================================================================


# ----------------------------------------------------------------------------
# Main Certificate Shape
# ----------------------------------------------------------------------------
# Targets all instances of era:Certificate and applies Part A-specific validation rules.

era-sh:SafetyCertificateAShape 
    a sh:NodeShape ;
    sh:targetClass era:Certificate ;
    sh:nodeKind sh:IRI ;
    dcterms:relation era-permission-types:SafetyCertificateA .

# ----------------------------------------------------------------------------
# Certificate Type and Identification
# ----------------------------------------------------------------------------

# The dcterms:type of these Certificates must be SafetyCertificateA
era-sh:SafetyCertificateAShape sh:property era-sh:SCATypeShape .
era-sh:SCATypeShape a sh:PropertyShape ;
    sh:path dcterms:type  ;
    era:affectedProperty dcterms:type  ;
    sh:name "Certificate Type" ;
    sh:nodeKind sh:IRI ;
    sh:hasValue era-permission-types:SafetyCertificateA ;
    sh:maxCount 1 ;
    sh:minCount 1 ;
    sh:description "This Certificate must be typed as Safety Certificate Part A" ;
    sh:message "A Safety Certificate Type A MUST have `dcterms:type era-permission-types:SafetyCertificateA`"@en ;
    sh:severity sh:Violation .

# The dcterms:identifier follows the EU Identification Number format
era-sh:SafetyCertificateAShape sh:property era-sh:SCAIdentifierShape .
era-sh:SCAIdentifierShape a sh:PropertyShape ;
    sh:path dcterms:identifier  ;
    era:affectedProperty dcterms:identifier  ;
    sh:name "EU Identification Number" ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:minCount 1 ; 
    sh:maxCount 1 ;
    sh:pattern "^[A-Z]{2}11[2-9][0-9]{3}[0-9]{4}$" ;
    sh:description "Format: CC11YYYYNNNN where CC=country code, 11=certificate type, YYYY=year, NNNN=sequential number" ;
    sh:message "A Safety Certificate Type A MUST have ONE valid identifier matching pattern CC11YYYYNNNN"@en ;
    sh:severity sh:Violation .

# Optional reference to an ECM certificate (Entity in Charge of Maintenance)
era-sh:SafetyCertificateAShape sh:property era-sh:SCAECMCertShape .
era-sh:SCAECMCertShape a sh:PropertyShape ;
    sh:path era:relatedCertificate  ;
    era:affectedProperty era:relatedCertificate  ;
    sh:name "ECM Certificate Number" ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:minCount 0 ; sh:maxCount 1 ;
    sh:pattern "^[A-Z]{2}/31/\\d{4}/\\d{4}$" ;
    sh:description "Reference to related ECM Certificate with format CC/31/YYYY/NNNN" ;
    sh:message "A Safety Certificate Type A MAY refer to ONE ECM certificate identifier"@en ;
    sh:severity sh:Info .

# Internal identifier for administrative purposes
era-sh:SafetyCertificateAShape sh:property era-sh:SCAInternalIdentifierShape .
era-sh:SCAInternalIdentifierShape a sh:PropertyShape ;
    sh:path rdfs:label  ;
    era:affectedProperty rdfs:label  ;
    sh:name "Internal Identification Number" ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:minCount 1 ; 
    sh:maxCount 1 ;
    sh:description "Internal reference number used by the issuing NSA" ;
    sh:message "A Safety Certificate Type A SHOULD have an internal identifier"@en ;
    sh:severity sh:Warning .

# ----------------------------------------------------------------------------
# 1. Railway Undertaking (Certificate Holder)
# ----------------------------------------------------------------------------
# The certificate must be issued to a Railway Undertaking with appropriate role classification.

era-sh:SafetyCertificateAShape sh:property era-sh:SCARURoleShape .
era-sh:SCARURoleShape a sh:PropertyShape ;
    sh:path dcterms:audience  ;
    era:affectedProperty dcterms:audience  ;
    sh:name "Certified Railway Undertaking (1)" ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:node era-sh:RailwayUndertakingRoleShape ;
    sh:class era:OrganisationRole ;
    sh:description "Links to the OrganisationRole defining the Railway Undertaking"@en ;
    sh:message "A Safety Certificate Type A MUST link to the OrganisationRole Railway Undertaking"@en ;
    sh:severity sh:Violation .

# Validates that the organisation role is a Railway Undertaking (RU)
era-sh:RailwayUndertakingRoleShape a sh:NodeShape ;
    sh:nodeKind sh:IRI ;
    sh:property era-sh:RailwayUndertakingIsFromRUShape-SCA ;
    sh:description "Defines the role classification for Railway Undertakings" .

era-sh:RailwayUndertakingIsFromRUShape-SCA a sh:PropertyShape ;
    sh:path era:hasOrganisationRole  ;
    era:affectedProperty era:hasOrganisationRole  ;
    sh:name "Railway Undertaking Role Type" ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:message "The OrganisationRole of a Safety Certificate Part A must apply to a Railway Undertaking (RU)"@en ;
    sh:nodeKind sh:IRI ;
    sh:severity sh:Violation ;
    sh:class skos:Concept ;
    sh:in ( era-organisation-roles:RU era-organisation-roles:RU-1 era-organisation-roles:RU-2 era-organisation-roles:RU-3 era-organisation-roles:RU-4 era-organisation-roles:RU-5 ) ;
    sh:description "Specifies the type of Railway Undertaking operations" .

# ----------------------------------------------------------------------------
# 2. Certificate Issuing Organisation
# ----------------------------------------------------------------------------
# The certificate must be issued by a National Safety Authority (NSA) with appropriate role classification.

era-sh:SafetyCertificateAShape sh:property era-sh:SCA-NSA-Shape .
era-sh:SCA-NSA-Shape a sh:PropertyShape ;
    sh:path dcterms:creator  ;
    era:affectedProperty dcterms:creator  ;
    sh:name "Certificate Issuing Organisation (2)" ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:node era-sh:NSAOrganisationRoleShape ;
    sh:class era:OrganisationRole ;
    sh:description "Links to the OrganisationRole defining the Certificate Issuing Organisation"@en ;
    sh:message "A Safety Certificate Type A MUST link to the OrganisationRole Certificate Issuing Organisation"@en ;
    sh:severity sh:Violation .

# Validates that the organisation role is a Railway Undertaking (RU)
era-sh:NSAOrganisationRoleShape a sh:NodeShape ;
    sh:nodeKind sh:IRI ;
    sh:property era-sh:NSAOrganisationIsFromRUShape-SCA ;
    sh:description "Defines the role classification for National Safety Authorities" .

era-sh:NSAOrganisationIsFromRUShape-SCA a sh:PropertyShape ;
    sh:path era:hasOrganisationRole  ;
    era:affectedProperty era:hasOrganisationRole  ;
    sh:name "National Safety Authority Role Type" ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:message "The OrganisationRole of a Safety Certificate Part A must be issued by a National Safety Authority (NSA)"@en ;
    sh:nodeKind sh:IRI ;
    sh:severity sh:Violation ;
    sh:class skos:Concept ;
    sh:hasValue era-organisation-roles:NSA-RU-SCB ;     # National Safety Authority as SC Body
    sh:description "Specifies National Safety Authority role" .

# ----------------------------------------------------------------------------
# Type and Extent of Service
# ----------------------------------------------------------------------------
# Defines the operational scope and capacity of the Railway Undertaking.

# High-speed operation authorization
era-sh:SafetyCertificateAShape sh:property era-sh:SCA-RU-HS-OperationShape .
era-sh:SCA-RU-HS-OperationShape a sh:PropertyShape ;
    sh:name "High-speed Operation" ;
    sh:path era:coversHighSpeed  ;
    era:affectedProperty era:coversHighSpeed  ;
    sh:datatype xsd:boolean ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:description "Indicates whether the RU is authorized for high-speed operations."@en ;
    sh:message "A Safety Certificate Type A MUST indicate whether the RU is authorized for high-speed operations"@en ;
    sh:severity sh:Violation .

# Dangerous goods (RID) operation authorization
era-sh:SafetyCertificateAShape sh:property era-sh:SCA-RU-RID-OperationShape .
era-sh:SCA-RU-RID-OperationShape a sh:PropertyShape ;
    sh:name "Dangerous Goods Operation" ;
    sh:path era:coversDangerousGoods  ;
    era:affectedProperty era:coversDangerousGoods  ;
    sh:datatype xsd:boolean ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:description "Indicates whether the RU is authorized for dangerous goods transport according to RID regulations"@en ;
    sh:message "A Safety Certificate Type A MUST indicate whether the RU is authorized for dangerous goods operations"@en ;
    sh:severity sh:Violation .

# Shunting-only operations
era-sh:SafetyCertificateAShape sh:property era-sh:SCA-RU-Shunting-OperationShape .
era-sh:SCA-RU-Shunting-OperationShape a sh:PropertyShape ;
    sh:name "Shunting Only" ;
    sh:path era:coversOnlyShunting  ;
    era:affectedProperty era:coversOnlyShunting  ;
    sh:datatype xsd:boolean ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:description "Indicates whether the RU is restricted to shunting operations only (no mainline running)"@en ;
    sh:message "A Safety Certificate Type A MUST indicate whether the RU is authorized for shunting operations only"@en ;
    sh:severity sh:Violation .

era-sh:SafetyCertificateAShape sh:property era-sh:SCA-RU-ServiceStartShape .
era-sh:SCA-RU-ServiceStartShape a sh:PropertyShape ;
   sh:name "Service/s to begin in" ;
   sh:path dcterms:dateAccepted  ;
   era:affectedProperty dcterms:dateAccepted  ;
   sh:datatype xsd:date ;
   sh:minCount 0 ; sh:maxCount 1 ;
   sh:description "The date when the service/s will begin."@en ;
   sh:message "A Safety Certificate Type A MAY indicate the date when the service/s will begin"@en ;
   sh:severity sh:Info .

era-sh:SafetyCertificateAShape sh:property era-sh:SCA-RU-VolumePassengerShape, era-sh:SCA-RU-VolumeFreightShape .
era-sh:SCA-RU-VolumePassengerShape a sh:PropertyShape ;
    sh:name "Transportation Volume Passenger" ;
    sh:path era:transportationVolumePassenger  ;
    era:affectedProperty era:transportationVolumePassenger  ;
    sh:in ( "Less than 200 MPkm/year" "200MPkm/year and more" ) ;
    sh:minCount 0 ; sh:maxCount 1 ;
    sh:datatype xsd:string ;
    sh:description "The transportation volume for passenger services."@en ;
    sh:message "A Safety Certificate Type A MAY describe ONE transportation volume for passenger services"@en ;
    sh:severity sh:Info .

era-sh:SCA-RU-VolumeFreightShape a sh:PropertyShape ;
    sh:name "Transportation Volume Freight" ;
    sh:path era:transportationVolumeFreight  ;
    era:affectedProperty era:transportationVolumeFreight  ;
    sh:in ( "Less than 500 MTonneskm/year" "500 MTonneskm/year and more" ) ;
    sh:minCount 0 ; sh:maxCount 1 ;
    sh:datatype xsd:string ;
    sh:description "The transportation volume for freight services."@en ;
    sh:message "A Safety Certificate Type A MAY describe ONE transportation volume for freight services"@en ;
    sh:severity sh:Info .

# ----------------------------------------------------------------------------
# Applicable National Legislation
# ----------------------------------------------------------------------------
# National Acts under which the certificate is issued.
era-sh:SafetyCertificateAShape sh:property era-sh:SCAApplicableLegislationShape .
era-sh:SCAApplicableLegislationShape a sh:PropertyShape ;
    sh:path dcterms:source  ;
    era:affectedProperty dcterms:source  ;
    sh:name "Applicable National Legislation (4)" ;
    sh:nodeKind sh:Literal ;
    sh:minCount 1 ; sh:maxCount 5 ;
    sh:datatype xsd:string ;
    sh:description "The national legislation under which the certificate is issued."@en ;
    sh:message "A Safety Certificate Type A MUST refer to at least ONE applicable national legislation"@en ;
    sh:severity sh:Violation .

# ----------------------------------------------------------------------------
# Additional comments
# ----------------------------------------------------------------------------
# National Acts under which the certificate is issued.
era-sh:SafetyCertificateAShape sh:property era-sh:SCAAdditionalCommentsShape .
era-sh:SCAAdditionalCommentsShape a sh:PropertyShape ;
    sh:path rdfs:comment  ;
    era:affectedProperty rdfs:comment  ;
    sh:name "Additional comments (5)" ;
    sh:nodeKind sh:Literal ;
    sh:minCount 0 ; sh:maxCount 1 ;
    sh:datatype xsd:string ;
    sh:description "Additional comments related to the certificate."@en ;
    sh:message "A Safety Certificate Type A allows ONE Additional comment"@en ;
    sh:severity sh:Violation .

# ----------------------------------------------------------------------------
# Issue date
# ----------------------------------------------------------------------------
# Safety Certificates Part A issue date

era-sh:SafetyCertificateAShape sh:property era-sh:SCAIssueDateShape .
era-sh:SCAIssueDateShape a sh:PropertyShape ;
    sh:path dcterms:issued  ;
    era:affectedProperty dcterms:issued  ;
    sh:name "Issue date" ;
    sh:datatype xsd:date ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:nodeKind sh:Literal ;
    sh:description "Date on which the certificate is issued." ;
    sh:message "A Safety Certificate Type A MUST have ONE Issue date."@en ;
    sh:severity sh:Violation .

# ----------------------------------------------------------------------------
# Validity Period
# ----------------------------------------------------------------------------
# Safety Certificates Part A are valid for up to 5 years.

era-sh:SafetyCertificateAShape sh:property era-sh:SCAValidityShape .
era-sh:SCAValidityShape a sh:PropertyShape ;
    sh:path dcterms:valid  ;
    era:affectedProperty dcterms:valid  ;
    sh:name "Validity Period" ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:qualifiedValueShape era-sh:StartEndValidityShape ;
    sh:qualifiedMinCount 1 ; sh:qualifiedMaxCount 1 ;
    sh:description "Defines the time interval during which the certificate is valid (max 5 years)" ;
    sh:message "A Safety Certificate Type A MUST have ONE Validity Interval (start and end date)."@en ;
    sh:severity sh:Violation .

# ============================================================================
# ERADIS Safety Certificate Part B Shapes
# ============================================================================
# This file defines SHACL shapes for validating Safety Certificates Part B.
#
# A Safety Certificate Part B validates that a Railway Undertaking's (RU)
# Safety Management System (SMS) complies with regulatory requirements.
# It is issued by a National Safety Authority (NSA).
#
# Key validation areas:
# - Certificate identification and type
# - Railway Undertaking role and authorization
# - Validity period (start and end dates)
# - Type and extent of service (passenger/freight, volumes, operations)
# - Related Part A certificate reference
# - Applicable national legislation
# - Specific conditions and obligations
# ============================================================================


# ----------------------------------------------------------------------------
# Main Certificate Shape
# ----------------------------------------------------------------------------
# Targets all instances of era:Certificate and applies Part B-specific validation rules.

era-sh:SafetyCertificateBShape 
    a sh:NodeShape ;
    sh:targetClass era:Certificate ;
    sh:nodeKind sh:IRI ;
    dcterms:relation era-permission-types:SafetyCertificateB .

# ----------------------------------------------------------------------------
# Certificate Type and Identification
# ----------------------------------------------------------------------------

# The dcterms:type of these Certificates must be SafetyCertificateB
era-sh:SafetyCertificateBShape sh:property era-sh:SCBTypeShape .
era-sh:SCBTypeShape a sh:PropertyShape ;
    sh:path dcterms:type  ;
    era:affectedProperty dcterms:type  ;
    sh:name "Certificate Type" ;
    sh:nodeKind sh:IRI ;
    sh:hasValue era-permission-types:SafetyCertificateB ;
    sh:maxCount 1 ;
    sh:minCount 1 ;
    sh:description "This Certificate must be typed as Safety Certificate Part B" ;
    sh:message "A Safety Certificate Type B MUST have `dcterms:type era-permission-types:SafetyCertificateB`"@en ;
    sh:severity sh:Violation .

# The dcterms:identifier follows the EU Identification Number format
era-sh:SafetyCertificateBShape sh:property era-sh:SCBIdentifierShape .
era-sh:SCBIdentifierShape a sh:PropertyShape ;
    sh:path dcterms:identifier  ;
    era:affectedProperty dcterms:identifier  ;
    sh:name "EU Identification Number" ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:minCount 1 ; 
    sh:maxCount 1 ;
    sh:pattern "^[A-Z]{2}12[2-9][0-9]{3}[0-9]{4}$" ;
    sh:description "Format: CC12YYYYNNNN where CC=country code, 12=certificate type, YYYY=year, NNNN=sequential number" ;
    sh:message "A Safety Certificate Type B MUST have ONE valid identifier matching pattern CC12YYYYNNNN"@en ;
    sh:severity sh:Violation .

# Mandatroy reference to Part A certificate
era-sh:SafetyCertificateBShape sh:property era-sh:SCBSCARefShape .
era-sh:SCBSCARefShape a sh:PropertyShape ;
    sh:path era:relatedCertificate  ;
    era:affectedProperty era:relatedCertificate  ;
    sh:name "Safety Certificate Part A - Certificate Number" ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:pattern "^[A-Z]{2}/11/\\d{4}/\\d{4}$" ;
    sh:description "Reference to the Certificate Part A with format CC/11/YYYY/NNNN" ;
    sh:message "A Safety Certificate Type B MUST refer to ONE Part A certificate identifier"@en ;
    sh:severity sh:Info .

# ----------------------------------------------------------------------------
# 1. Railway Undertaking (Certificate Holder)
# ----------------------------------------------------------------------------
# The certificate must be issued to a Railway Undertaking with appropriate role classification.

era-sh:SafetyCertificateBShape sh:property era-sh:SCBRURoleShape .
era-sh:SCBRURoleShape a sh:PropertyShape ;
    sh:path dcterms:audience  ;
    era:affectedProperty dcterms:audience  ;
    sh:name "Certified Railway Undertaking (1)" ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:node era-sh:RailwayUndertakingRoleShape ;
    sh:class era:OrganisationRole ;
    sh:description "Links to the OrganisationRole defining the Railway Undertaking"@en ;
    sh:message "A Safety Certificate Type A MUST link to the OrganisationRole Railway Undertaking"@en ;
    sh:severity sh:Violation .

# Validates that the organisation role is a Railway Undertaking (RU)
era-sh:RailwayUndertakingRoleShape a sh:NodeShape ;
    sh:nodeKind sh:IRI ;
    sh:property era-sh:RailwayUndertakingIsFromRUShape-SCB ;
    sh:description "Defines the role classification for Railway Undertakings" .

era-sh:RailwayUndertakingIsFromRUShape-SCB a sh:PropertyShape ;
    sh:path era:hasOrganisationRole  ;
    era:affectedProperty era:hasOrganisationRole  ;
    sh:name "Railway Undertaking Role Type" ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:message "The OrganisationRole of a Safety Certificate Part B must apply to a Railway Undertaking (RU)"@en ;
    sh:nodeKind sh:IRI ;
    sh:severity sh:Violation ;
    sh:class skos:Concept ;
    sh:in ( era-organisation-roles:RU era-organisation-roles:RU-1 era-organisation-roles:RU-2 era-organisation-roles:RU-3 era-organisation-roles:RU-4 era-organisation-roles:RU-5 ) ;
    sh:description "Specifies the type of Railway Undertaking operations" .

# ----------------------------------------------------------------------------
# 1. Certificate Issuing Organisation
# ----------------------------------------------------------------------------
# The certificate must be issued by a National Safety Authority (NSA) with appropriate role classification.

era-sh:SafetyCertificateBShape sh:property era-sh:SCB-NSA-Shape .
era-sh:SCB-NSA-Shape a sh:PropertyShape ;
    sh:path dcterms:creator  ;
    era:affectedProperty dcterms:creator  ;
    sh:name "Certificate Issuing Organisation (2)" ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:node era-sh:NSAOrganisationRoleShape ;
    sh:class era:OrganisationRole ;
    sh:description "Links to the OrganisationRole defining the Certificate Issuing Organisation"@en ;
    sh:message "A Safety Certificate Type A MUST link to the OrganisationRole Certificate Issuing Organisation"@en ;
    sh:severity sh:Violation .

# Validates that the organisation role is a Railway Undertaking (RU)
era-sh:NSAOrganisationRoleShape a sh:NodeShape ;
    sh:nodeKind sh:IRI ;
    sh:property era-sh:NSAOrganisationIsFromRUShape-SCB ;
    sh:description "Defines the role classification for National Safety Authorities" .

era-sh:NSAOrganisationIsFromRUShape-SCB a sh:PropertyShape ;
    sh:path era:hasOrganisationRole  ;
    era:affectedProperty era:hasOrganisationRole  ;
    sh:name "National Safety Authority Role Type" ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:message "The OrganisationRole of a Safety Certificate Part B must be issued by a National Safety Authority (NSA)"@en ;
    sh:nodeKind sh:IRI ;
    sh:severity sh:Violation ;
    sh:class skos:Concept ;
    sh:hasValue era-organisation-roles:NSA-RU-SCB ;     # National Safety Authority as SC Body
    sh:description "Specifies National Safety Authority role" .

# ----------------------------------------------------------------------------
# Type and Extent of Service
# ----------------------------------------------------------------------------
# Defines the operational scope and capacity of the Railway Undertaking.

# High-speed operation authorization
era-sh:SafetyCertificateBShape sh:property era-sh:SCB-RU-HS-OperationShape .
era-sh:SCB-RU-HS-OperationShape a sh:PropertyShape ;
    sh:name "High-speed Operation" ;
    sh:path era:coversHighSpeed  ;
    era:affectedProperty era:coversHighSpeed  ;
    sh:datatype xsd:boolean ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:description "Indicates whether the RU is authorized for high-speed operations."@en ;
    sh:message "A Safety Certificate Type A MUST indicate whether the RU is authorized for high-speed operations"@en ;
    sh:severity sh:Violation .

# Dangerous goods (RID) operation authorization
era-sh:SafetyCertificateBShape sh:property era-sh:SCB-RU-RID-OperationShape .
era-sh:SCB-RU-RID-OperationShape a sh:PropertyShape ;
    sh:name "Dangerous Goods Operation" ;
    sh:path era:coversDangerousGoods  ;
    era:affectedProperty era:coversDangerousGoods  ;
    sh:datatype xsd:boolean ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:description "Indicates whether the RU is authorized for dangerous goods transport according to RID regulations"@en ;
    sh:message "A Safety Certificate Type A MUST indicate whether the RU is authorized for dangerous goods operations"@en ;
    sh:severity sh:Violation .

# Shunting-only operations
era-sh:SafetyCertificateBShape sh:property era-sh:SCB-RU-Shunting-OperationShape .
era-sh:SCB-RU-Shunting-OperationShape a sh:PropertyShape ;
    sh:name "Shunting Only" ;
    sh:path era:coversOnlyShunting  ;
    era:affectedProperty era:coversOnlyShunting  ;
    sh:datatype xsd:boolean ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:description "Indicates whether the RU is restricted to shunting operations only (no mainline running)"@en ;
    sh:message "A Safety Certificate Type A MUST indicate whether the RU is authorized for shunting operations only"@en ;
    sh:severity sh:Violation .

# ----------------------------------------------------------------------------
# Lines operated
# ----------------------------------------------------------------------------
# Lines operated under the scope to the certificate.
era-sh:SafetyCertificateBShape sh:property era-sh:SCBLinesOperatedShape .
era-sh:SCBLinesOperatedShape a sh:PropertyShape ;
    sh:path dcterms:spatial  ;
    era:affectedProperty dcterms:spatial  ;
    sh:name "Lines operated" ;
    sh:nodeKind sh:Literal ;
    sh:minCount 0 ; sh:maxCount 25 ;
    sh:datatype xsd:string ;
    sh:description "Lines operated under the scope of the certificate."@en ;
    sh:message "A Safety Certificate Type B allows several lines operated to be specified."@en ;
    sh:severity sh:Violation .

# ----------------------------------------------------------------------------
# Applicable National Legislation
# ----------------------------------------------------------------------------
# National Acts under which the certificate is issued.
era-sh:SafetyCertificateBShape sh:property era-sh:SCBApplicableLegislationShape .
era-sh:SCBApplicableLegislationShape a sh:PropertyShape ;
    sh:path dcterms:source  ;
    era:affectedProperty dcterms:source  ;
    sh:name "Applicable National Legislation (4)" ;
    sh:nodeKind sh:Literal ;
    sh:minCount 1 ; sh:maxCount 5 ;
    sh:datatype xsd:string ;
    # dash:singleLine is from DASH Data Shapes vocabulary (not SHACL Core) - used for UI hints
   # dash:singleLine false ;
    sh:description "The national legislation under which the certificate is issued."@en ;
    sh:message "A Safety Certificate Type A MUST refer to at least ONE applicable national legislation"@en ;
    sh:severity sh:Violation .


# ----------------------------------------------------------------------------
# Issue date
# ----------------------------------------------------------------------------
# Safety Certificates Part B issue date

era-sh:SafetyCertificateBShape sh:property era-sh:SCBIssueDateShape .
era-sh:SCBIssueDateShape a sh:PropertyShape ;
    sh:path dcterms:issued  ;
    era:affectedProperty dcterms:issued  ;
    sh:name "Issue date" ;
    sh:datatype xsd:date ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:nodeKind sh:Literal ;
    sh:description "Date on which the certificate is issued." ;
    sh:message "A Safety Certificate Type A MUST have ONE Issue date."@en ;
    sh:severity sh:Violation .

# ----------------------------------------------------------------------------
# Validity Period
# ----------------------------------------------------------------------------
# Safety Certificates Part B are valid for up to 5 years.

era-sh:SafetyCertificateBShape sh:property era-sh:SCBValidityShape .
era-sh:SCBValidityShape a sh:PropertyShape ;
    sh:path dcterms:valid  ;
    era:affectedProperty dcterms:valid  ;
    sh:name "Validity Period" ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:qualifiedValueShape era-sh:StartEndValidityShape ;
    sh:qualifiedMinCount 1 ; sh:qualifiedMaxCount 1 ;
    sh:description "Defines the time interval during which the certificate is valid (max 5 years)" ;
    sh:message "A Safety Certificate Type A MUST have ONE Validity Interval (start and end date)."@en ;
    sh:severity sh:Violation .

# The internal reference ID of these Certificates
era-sh:SafetyCertificateBShape sh:property era-sh:SCBInternalIdentifierShape .
era-sh:SCBInternalIdentifierShape a sh:PropertyShape ;
    sh:path rdfs:label  ;
    era:affectedProperty rdfs:label  ;
    sh:name "Internal Identification Number"@en ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:minCount 1 ; 
    sh:maxCount 1 ;
    sh:description "A Safety Certificate Part B should have an internal identifier." ;
    sh:message "A Safety Certificate Type A SHOULD have an internal identifier"@en ;
    sh:severity sh:Warning .

# ============================================================================
# ERADIS Single Safety Certificate Shapes
# ============================================================================
# This file defines SHACL shapes for validating Single Safety Certificates
# as defined in Commission Implementing Regulation (EU) 2018/763.
#
# A Single Safety Certificate validates that a Railway Undertaking's (RU)
# Safety Management System (SMS) complies with regulatory requirements.
#
# Key validation areas:
# - Certificate identification and type
# - Railway Undertaking role and authorization
# - Safety Certification Body role and authorization
# - Validity period (start and end dates)
# - Type and extent of service (passenger/freight, volumes, operations)
# - Previous Single Safety Certificates (renewals/updates)
# ============================================================================


# ----------------------------------------------------------------------------
# Main Certificate Shape
# ----------------------------------------------------------------------------
# Targets all instances of era:Certificate and applies Part A-specific validation rules.

era-sh:SingleSafetyCertificateShape 
    a sh:NodeShape ;
    sh:targetClass era:Certificate ; # subClassOf vpa:Permission
    sh:nodeKind sh:IRI ;
    dcterms:created "2025-10-14"^^xsd:date ;
    dcterms:relation era-permission-types:SingleSafetyCertificate . 

# ----------------------------------------------------------------------------
# 1. Certificate Type and Identification
# ----------------------------------------------------------------------------

# The dcterms:type of these Certificates must be SingleSafetyCertificate
era-sh:SingleSafetyCertificateShape sh:property era-sh:SSCTypeShape .
era-sh:SSCTypeShape a sh:PropertyShape ;
    sh:path dcterms:type  ;
    era:affectedProperty dcterms:type  ;
    sh:name "Certificate Type" ;
    sh:nodeKind sh:IRI ;
    sh:hasValue era-permission-types:SingleSafetyCertificate ;
    sh:maxCount 1 ;
    sh:minCount 1 ;
    sh:description "This Certificate must be typed as Single Safety Certificate" ;
    sh:message "A Single Safety Certificate MUST have `dcterms:type era-permission-types:SingleSafetyCertificate`"@en ;
    sh:severity sh:Violation .

# The dcterms:identifier follows the EU Identification Number format
era-sh:SingleSafetyCertificateShape sh:property era-sh:SSCIdentifierShape .
era-sh:SSCIdentifierShape a sh:PropertyShape ;
    sh:path dcterms:identifier  ;
    era:affectedProperty dcterms:identifier  ;
    sh:name "1.1 EU Identification Number" ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:minCount 1 ; 
    sh:maxCount 1 ;
    sh:pattern "^[A-Z]{2}10[2-9][0-9]{3}[0-9]{4}$" ;
    sh:description "Format: CC10YYYYNNNN where CC=country code, 10=certificate type, YYYY=year, NNNN=sequential number" ;
    sh:message "A Single Safety Certificate MUST have ONE valid identifier matching pattern CC10YYYYNNNN"@en ;
    sh:severity sh:Violation .

# Optional reference to a previous certificate (renewal or update)
era-sh:SingleSafetyCertificateShape sh:property era-sh:SSCPreviousCertificateShape .
era-sh:SSCPreviousCertificateShape a sh:PropertyShape ;
    sh:path era:relatedCertificate  ;
    era:affectedProperty era:relatedCertificate  ;
    sh:name "1.3 EIN of the previous certificate (in case of renewal or update only)" ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:minCount 0 ; sh:maxCount 1 ;
    sh:pattern "^[A-Z]{2}10[2-9][0-9]{3}[0-9]{4}$" ;
    sh:description "Reference to a previous certificate with format CC/10/YYYY/NNNN" ;
    sh:message "A Single Safety Certificate MAY refer to ONE previous certificate identifier"@en ;
    sh:severity sh:Info .

# ----------------------------------------------------------------------------
# 1.4 Validity Period
# ----------------------------------------------------------------------------
# Single Safety Certificates are valid for up to 5 years.

era-sh:SingleSafetyCertificateShape sh:property era-sh:SSCValidityShape .
era-sh:SSCValidityShape a sh:PropertyShape ;
    sh:path dcterms:valid  ;
    era:affectedProperty dcterms:valid  ;
    sh:name "1.4 Validity Period" ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:qualifiedValueShape era-sh:StartEndValidityShape ;
    sh:qualifiedMinCount 1 ; sh:qualifiedMaxCount 1 ;
    sh:description "Defines the time interval during which the certificate is valid (max 5 years)" ;
    sh:message "A Single Safety Certificate MUST have ONE Validity Interval (start and end date)."@en ;
    sh:severity sh:Violation .

# ----------------------------------------------------------------------------
# 2. Railway Undertaking (Certificate Holder)
# ----------------------------------------------------------------------------
# The certificate must be issued to a Railway Undertaking with appropriate role classification.

era-sh:SingleSafetyCertificateShape sh:property era-sh:SSCRURoleShape .
era-sh:SSCRURoleShape a sh:PropertyShape ;
    sh:path dcterms:audience  ;
    era:affectedProperty dcterms:audience  ;
    sh:name "2. Railway Undertaking" ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:node era-sh:RailwayUndertakingRoleShape ;
    sh:class era:OrganisationRole ;
    sh:description "Links to the OrganisationRole defining the Railway Undertaking"@en ;
    sh:message "A Single Safety Certificate MUST link to the OrganisationRole Railway Undertaking"@en ;
    sh:severity sh:Violation .

# Validates that the organisation role is a Railway Undertaking (RU)
era-sh:RailwayUndertakingRoleShape a sh:NodeShape ;
    sh:nodeKind sh:IRI ;
    sh:property era-sh:RailwayUndertakingIsFromRUShape-SCC ;
    sh:description "Defines the role classification for Railway Undertakings" .

era-sh:RailwayUndertakingIsFromRUShape-SCC a sh:PropertyShape ;
    sh:path era:hasOrganisationRole  ;
    era:affectedProperty era:hasOrganisationRole  ;
    sh:name "Railway Undertaking Role Type" ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:message "The OrganisationRole of a Single Safety Certificate must apply to a Railway Undertaking (RU)"@en ;
    sh:nodeKind sh:IRI ;
    sh:severity sh:Violation ;
    sh:class skos:Concept ;
    sh:in ( era-organisation-roles:RU era-organisation-roles:RU-1 era-organisation-roles:RU-2 era-organisation-roles:RU-3 era-organisation-roles:RU-4 era-organisation-roles:RU-5 ) ;
    sh:description "Specifies the type of Railway Undertaking operations" .

# ----------------------------------------------------------------------------
# 3. Safety Certification Body
# ----------------------------------------------------------------------------
# The certificate must be issued by a Safety Certification Body (SCB) with appropriate role classification.

era-sh:SingleSafetyCertificateShape sh:property era-sh:SSC-SCB-Shape .
era-sh:SSC-SCB-Shape a sh:PropertyShape ;
    sh:path dcterms:creator  ;
    era:affectedProperty dcterms:creator  ;
    sh:name "3. Safety Certification Body" ;
    sh:nodeKind sh:IRI ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
    sh:node era-sh:SCBOrganisationRoleShape ;
    sh:class era:OrganisationRole ;
    sh:description "Links to the OrganisationRole defining the Certificate Issuing Organisation"@en ;
    sh:message "A Single Safety Certificate MUST link to the OrganisationRole Certificate Issuing Organisation"@en ;
    sh:severity sh:Violation .

# Validates that the organisation role is a Railway Undertaking (RU)
era-sh:SCBOrganisationRoleShape a sh:NodeShape ;
    sh:nodeKind sh:IRI ;
    sh:property era-sh:SCBOrganisationIsFromRUShape ;
    sh:description "Defines the role classification for Safety Certification Bodies" .

era-sh:SCBOrganisationIsFromRUShape a sh:PropertyShape ;
    sh:path era:hasOrganisationRole  ;
    era:affectedProperty era:hasOrganisationRole  ;
    sh:name "Safety Certification Body Role Type" ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:message "The OrganisationRole of a Single Safety Certificate must be issued by a Safety Certification Body (NSA)"@en ;
    sh:nodeKind sh:IRI ;
    sh:severity sh:Violation ;
    sh:class skos:Concept ;
    sh:in ( era-organisation-roles:NSA-RU-SCB era-organisation-roles:AuthorisingEntity ) ;
    sh:description "Specifies Safety Certification Body role" .

# ----------------------------------------------------------------------------
# Type and Extent of Service
# ----------------------------------------------------------------------------
# Defines the operational scope and capacity of the Railway Undertaking.

# High-speed operation authorization
era-sh:SingleSafetyCertificateShape sh:property era-sh:SSC-RU-HS-OperationShape .
era-sh:SSC-RU-HS-OperationShape a sh:PropertyShape ;
    sh:name "High-speed Operation" ;
    sh:path era:coversHighSpeed  ;
    era:affectedProperty era:coversHighSpeed  ;
    sh:datatype xsd:boolean ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:description "Indicates whether the RU is authorized for high-speed operations."@en ;
    sh:message "A Single Safety Certificate MUST indicate whether the RU is authorized for high-speed operations"@en ;
    sh:severity sh:Violation .

# Dangerous goods (RID) operation authorization
era-sh:SingleSafetyCertificateShape sh:property era-sh:SSC-RU-RID-OperationShape .
era-sh:SSC-RU-RID-OperationShape a sh:PropertyShape ;
    sh:name "Dangerous Goods Operation" ;
    sh:path era:coversDangerousGoods  ;
    era:affectedProperty era:coversDangerousGoods  ;
    sh:datatype xsd:boolean ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:description "Indicates whether the RU is authorized for dangerous goods transport according to RID regulations"@en ;
    sh:message "A Single Safety Certificate MUST indicate whether the RU is authorized for dangerous goods operations"@en ;
    sh:severity sh:Violation .

# Shunting-only operations
era-sh:SingleSafetyCertificateShape sh:property era-sh:SSC-RU-Shunting-OperationShape .
era-sh:SSC-RU-Shunting-OperationShape a sh:PropertyShape ;
    sh:name "Shunting Only" ;
    sh:path era:coversOnlyShunting  ;
    era:affectedProperty era:coversOnlyShunting  ;
    sh:datatype xsd:boolean ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:description "Indicates whether the RU is restricted to shunting operations only (no mainline running)"@en ;
    sh:message "A Single Safety Certificate MUST indicate whether the RU is authorized for shunting operations only"@en ;
    sh:severity sh:Violation .

era-sh:SingleSafetyCertificateShape sh:property era-sh:SSC-RU-ServiceStartShape .
era-sh:SSC-RU-ServiceStartShape a sh:PropertyShape ;
   sh:name "Service/s to begin in" ;
   sh:path dcterms:dateAccepted  ;
   era:affectedProperty dcterms:dateAccepted  ;
   sh:datatype xsd:date ;
   sh:minCount 0 ; sh:maxCount 1 ;
   sh:description "The date when the service/s will begin."@en ;
   sh:message "A Single Safety Certificate MAY indicate the date when the service/s will begin"@en ;
   sh:severity sh:Info .

era-sh:SingleSafetyCertificateShape sh:property era-sh:SSC-RU-VolumePassengerShape, era-sh:SSC-RU-VolumeFreightShape .
era-sh:SSC-RU-VolumePassengerShape a sh:PropertyShape ;
    sh:name "Transportation Volume Passenger" ;
    sh:path era:transportationVolumePassenger  ;
    era:affectedProperty era:transportationVolumePassenger  ;
    sh:in ( "Less than 200 MPkm/year" "200MPkm/year and more" ) ;
    sh:minCount 0 ; sh:maxCount 1 ;
    sh:datatype xsd:string ;
    sh:description "The transportation volume for passenger services."@en ;
    sh:message "A Single Safety Certificate MAY describe ONE transportation volume for passenger services"@en ;
    sh:severity sh:Info .

era-sh:SSC-RU-VolumeFreightShape a sh:PropertyShape ;
    sh:name "Transportation Volume Freight" ;
    sh:path era:transportationVolumeFreight  ;
    era:affectedProperty era:transportationVolumeFreight  ;
    sh:in ( "Less than 500 MTonneskm/year" "500 MTonneskm/year and more" ) ;
    sh:minCount 0 ; sh:maxCount 1 ;
    sh:datatype xsd:string ;
    sh:description "The transportation volume for freight services."@en ;
    sh:message "A Single Safety Certificate MAY describe ONE transportation volume for freight services"@en ;
    sh:severity sh:Info .

# ----------------------------------------------------------------------------
# 4.3 Operations to border stations
# ----------------------------------------------------------------------------
# 4.3 Operations to border stations under the scope to the certificate.
era-sh:SafetyCertificateBShape sh:property era-sh:SCBOperationsToBorderStationsShape .
era-sh:SCBOperationsToBorderStationsShape a sh:PropertyShape ;
    sh:path era:borderPointStation ;
    era:affectedProperty era:borderPointStation ;
    sh:class era:OperationalPoint ;
    sh:or (
                 [ sh:class era:OperationalPoint ]
                 [ sh:class era:ReferenceBorderPoint ]
    ) ;
    sh:name "4.3 Operations to border stations" ;
    sh:nodeKind sh:IRI ;
    sh:minCount 0 ; sh:maxCount 25 ;
    sh:description "4.3 Operations to border stations under the scope of the certificate."@en ;
    sh:message "A Single Safety Certificate allows several Operations to border stations to be specified."@en ;
    sh:severity sh:Violation .

# ----------------------------------------------------------------------------
# 4.2 Area of operation
# ----------------------------------------------------------------------------
# 4.2 Area of operation under the scope to the certificate.
era-sh:SafetyCertificateBShape sh:property era-sh:SCBAreaOfOperationShape .
era-sh:SCBAreaOfOperationShape a sh:PropertyShape ;
    sh:path era:areaOfOperation ;
    era:affectedProperty era:areaOfOperation ;
    sh:name "4.2 Area of operation" ;
    sh:nodeKind sh:IRI ;
    sh:minCount 0 ; sh:maxCount 25 ;
    sh:class skos:Concept ;
    sh:description "4.2 Area of operation under the scope of the certificate."@en ;
    sh:message "A Single Safety Certificate allows several Areas of operation to be specified."@en ;
    sh:severity sh:Violation .

# ----------------------------------------------------------------------------
# 4.5 Applicable national legislation
# ----------------------------------------------------------------------------
# National Acts under which the certificate is issued.
era-sh:SingleSafetyCertificateShape sh:property era-sh:SSCApplicableLegislationShape .
era-sh:SSCApplicableLegislationShape a sh:PropertyShape ;
    sh:path dcterms:source  ;
    era:affectedProperty dcterms:source  ;
    sh:name "4.5 Applicable National Legislation" ;
    sh:nodeKind sh:Literal ;
    sh:minCount 1 ; sh:maxCount 5 ;
    sh:datatype xsd:string ;
    # dash:singleLine is from DASH Data Shapes vocabulary (not SHACL Core) - used for UI hints
    #dash:singleLine false ;
    sh:description "The national legislation under which the certificate is issued."@en ;
    sh:message "A Single Safety Certificate MUST refer to at least ONE applicable national legislation"@en ;
    sh:severity sh:Violation .

# ----------------------------------------------------------------------------
# 4.6 Additional information
# ----------------------------------------------------------------------------
# National Acts under which the certificate is issued.
era-sh:SingleSafetyCertificateShape sh:property era-sh:SSCAdditionalCommentsShape .
era-sh:SSCAdditionalCommentsShape a sh:PropertyShape ;
    sh:path rdfs:comment  ;
    era:affectedProperty rdfs:comment  ;
    sh:name "4.6 Additional information" ;
    sh:nodeKind sh:Literal ;
    sh:minCount 0 ; sh:maxCount 1 ;
    # dash:singleLine is from DASH Data Shapes vocabulary (not SHACL Core) - used for UI hints
    #dash:singleLine false ;
    sh:datatype xsd:string ;
    sh:description "Additional information related to the certificate."@en ;
    sh:message "A Single Safety Certificate allows ONE Additional comment"@en ;
    sh:severity sh:Violation .

# ----------------------------------------------------------------------------
# 5 Issue date
# ----------------------------------------------------------------------------
# Single Safety Certificates issue date

era-sh:SingleSafetyCertificateShape sh:property era-sh:SSCIssueDateShape .
era-sh:SSCIssueDateShape a sh:PropertyShape ;
    sh:path dcterms:issued  ;
    era:affectedProperty dcterms:issued  ;
    sh:name "5. Issue date" ;
    sh:datatype xsd:date ;
    sh:minCount 1 ; sh:maxCount 1 ;
    sh:nodeKind sh:Literal ;
    sh:description "Date on which the certificate is issued." ;
    sh:message "A Single Safety Certificate MUST have ONE Issue date."@en ;
    sh:severity sh:Violation .

# Internal identifier for administrative purposes
era-sh:SingleSafetyCertificateShape sh:property era-sh:SSCInternalIdentifierShape .
era-sh:SSCInternalIdentifierShape a sh:PropertyShape ;
    sh:path rdfs:label  ;
    era:affectedProperty rdfs:label  ;
    sh:name "Internal Identification Number" ;
    sh:datatype xsd:string ;
    sh:nodeKind sh:Literal ;
    sh:minCount 1 ; 
    sh:maxCount 1 ;
    sh:description "Internal reference number used by the issuing NSA" ;
    sh:message "A Single Safety Certificate SHOULD have an internal identifier"@en ;
    sh:severity sh:Warning .



era-sh:OrganisationRoleShape
	a sh:NodeShape ;
    sh:nodeKind sh:IRI ;
	sh:targetClass era:OrganisationRole.

era-sh:BodyShape
	a sh:NodeShape ;
    sh:nodeKind sh:IRI ;
	sh:targetClass era:Body.

era-sh:SiteShape 
    a sh:NodeShape ;
    sh:nodeKind sh:IRI ;
	sh:targetClass org:Site.

era-sh:AddressShape 
    a sh:NodeShape ;
    sh:nodeKind sh:IRI ;
	sh:targetClass locn:Address.
    
# roleOf
era-sh:OrganisationRoleShape sh:property era-sh:RoleOf .

# role
era-sh:BodyShape sh:property era-sh:Role .

# hasOrganisationRole
era-sh:OrganisationRoleShape sh:property era-sh:HasOrganisationRole .

# hasOrganisationRole
era-sh:OrganisationRoleShape sh:sparql era-sh:HasOrganisationRoleSKOS.

# organisationSize
era-sh:BodyShape sh:property era-sh:OrganisationSize .
era-sh:OrganisationSize
	a sh:PropertyShape;
    era:affectedClass era:Body;
    era:affectedProperty era:organisationSize ;
    era:scope "local";
	rdfs:comment "The size of a given organisation or body."@en ;
	sh:path era:organisationSize  ;
	era:affectedProperty era:organisationSize  ;
    sh:maxCount 1;
	sh:nodeKind sh:IRI ;
	sh:severity sh:Violation ;
    sh:class skos:Concept ;
	sh:message "organisationSize: Each Body can have up to 1 organisation size which must be represented as an IRI which is a SKOS concept. This error may be due to having more than one value, or having a value that is not an IRI, or a value that is an IRI but not a SKOS concept."@en .


# organisationSize
era-sh:BodyShape sh:sparql era-sh:OrganisationSizeSKOS.
era-sh:OrganisationSizeSKOS
	a sh:SPARQLConstraint ;
    era:affectedProperty era:organisationSize;
    era:affectedClass  era:Body;
    era:scope "local";
    rdfs:comment "The size of a given organisation or body."@en ;
	sh:severity sh:Violation ;
    sh:message "Indication of the organisationSize: The Body {$this} has a value {?concept} that is not one of the predefined values and cannot be converted into a SKOS concept on this list: http://data.europa.eu/949/concepts/org-size/OrgSizes."@en ;
	sh:select """
	PREFIX era: <http://data.europa.eu/949/>
	PREFIX skos: <http://www.w3.org/2004/02/skos/core#>

    SELECT $this  ?concept
    WHERE {
        $this era:organisationSize ?concept .
        era:organisationSize era:inSkosConceptScheme ?conceptScheme .
		FILTER NOT EXISTS{         
  			?concept skos:inScheme ?conceptScheme .
		}
	}
""" .


# rdfs label 
era-sh:BodyShape sh:property era-sh:Label .
era-sh:Label
	a sh:PropertyShape;
    era:affectedClass era:Body ;
    era:affectedProperty rdfs:label ;
    era:scope "local";
	rdfs:comment "The label of an entity "@en ;
	sh:path rdfs:label  ;
	era:affectedProperty rdfs:label  ;
    sh:minCount 1;
    sh:maxCount 1;
	sh:datatype xsd:string ;
	sh:severity sh:Violation ;
	sh:message "rdfs:label: The era:Body must have a label using rdfs. This error may be due to not having a value, having more than one value, or having a value that is not a string."@en .

# goodrel:vatID
era-sh:BodyShape sh:property era-sh:VatID .
era-sh:VatID
	a sh:PropertyShape;
    era:affectedClass era:Body ;
    era:affectedProperty goodrel:vatID ;
    era:scope "local";
	rdfs:comment "The VAT Identifier "@en ;
	sh:path goodrel:vatID  ;
	era:affectedProperty goodrel:vatID  ;
    sh:maxCount 1;
	sh:datatype xsd:string ;
	sh:severity sh:Warning ;
	sh:message "vatID: The era:Body should have a at most one vatID. This error may be due to not having a value, having more than one value, or having a value that is not a string."@en .

# org:identifier
era-sh:BodyShape sh:property era-sh:Identifier .
era-sh:Identifier
	a sh:PropertyShape;
    era:affectedClass era:Body ;
    era:affectedProperty org:identifier ;
    era:scope "local";
	rdfs:comment "The ORG indentifier "@en ;
	sh:path org:identifier  ;
	era:affectedProperty org:identifier  ;
    sh:minCount 1;
    sh:maxCount 1;
	sh:datatype xsd:string ;
	sh:severity sh:Violation ;
	sh:message "orgIdentifier: The era:Body must have a at most one org identifier. This error may be due to not having a value, having more than one value, or having a value that is not a string."@en .

#hasSite
era-sh:BodyShape sh:property era-sh:HasSite .
era-sh:HasSite
	a sh:PropertyShape;
    era:affectedClass era:Body;
    era:affectedProperty org:hasSite ;
    era:scope "local";
	rdfs:comment "The link to the site of the era:Body"@en ;
	sh:path org:hasSite  ;
	era:affectedProperty org:hasSite  ;
    sh:minCount 1;
	sh:nodeKind sh:IRI ;
    sh:class org:Site ;
	sh:severity sh:Violation ;
	sh:message "hasSite: The organisation site must be represented as an IRI. This error may be due to not having a value,  or having a value that is not an IRI."@en .

#siteShape

era-sh:SiteShape sh:property era-sh:Label  .
#siteAddress

era-sh:SiteShape sh:property era-sh:SiteAddress  .

era-sh:SiteAddress 
    a sh:PropertyShape;
    era:affectedClass org:Site ;
    era:affectedProperty org:siteAddress ;
    era:scope "local";
	rdfs:comment "The link to the site address"@en ;
	sh:path org:siteAddress  ;
	era:affectedProperty org:siteAddress  ;
    sh:minCount 1;
    sh:maxCount 1;
	sh:nodeKind sh:IRI ;
    sh:class locn:Address ;
	sh:severity sh:Violation ;
	sh:message "hasSite: The organisation site must be represented as an IRI. This error may be due to not having a value,  or having a value that is not an IRI."@en .

## Address shape
# adminUnitL1
era-sh:AddressShape sh:property era-sh:AdminUnitL1  .
era-sh:AdminUnitL1 
    a sh:PropertyShape;
    era:affectedClass locn:Address;
    era:affectedProperty locn:adminUnitL1 ;
    era:scope "local";
   	rdfs:comment "Indicates the country of the address" ; 
	sh:path locn:adminUnitL1  ;
	era:affectedProperty locn:adminUnitL1  ;
    sh:minCount 1 ;
    sh:maxCount 1 ;
	sh:nodeKind sh:IRI;
	sh:severity sh:Violation ;
	sh:message "locn:adminUnitL1:  Each site address must have exactly one country. This error may be due to having a Site without or with more than one adminUnitL1 or it value is not a Concept."@en .


era-sh:AddressShape sh:sparql era-sh:AdminUnitL1SKOS  .
era-sh:AdminUnitL1SKOS
	a sh:SPARQLConstraint ;
    era:affectedClass locn:Address;
    era:affectedProperty locn:adminUnitL1 ;
    era:scope "local";
   	rdfs:comment "Indicates the country in which the site address." ;
	sh:severity sh:Violation ;
	sh:message "Country in which the Site address resides: The Address {$this} has a value {?concept} that is not one of the predefined values and cannot be converted into a SKOS concept on this list: http://publications.europa.eu/resource/authority/country."@en ;
	sh:select """
	PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
	PREFIX locn: <http://w3.org/ns/locn#>

    SELECT $this ?concept
    WHERE {
 		$this locn:adminUnitL1 ?concept .
		FILTER NOT EXISTS{         
  			?concept skos:inScheme <http://publications.europa.eu/resource/authority/country> .
		}
	}
""" .

#postCode
era-sh:AddressShape sh:property era-sh:PostCode .
era-sh:PostCode
	a sh:PropertyShape;
    era:affectedClass locn:Address ;
    era:affectedProperty locn:postCode ;
    era:scope "local";
	rdfs:comment "The postCode of a location "@en ;
	sh:path locn:postCode  ;
	era:affectedProperty locn:postCode  ;
    sh:minCount 1;
    sh:maxCount 1;
	sh:datatype xsd:string ;
	sh:severity sh:Violation ;
	sh:message "postCode: The Address must have exactly one postCode. This error may be due to not having a value, having more than one value, or having a value that is not a string."@en .

#postName
era-sh:AddressShape sh:property era-sh:PostName .
era-sh:PostName
	a sh:PropertyShape;
    era:affectedClass locn:Address ;
    era:affectedProperty locn:postName ;
    era:scope "local";
	rdfs:comment "The postName of a location "@en ;
	sh:path locn:postName  ;
	era:affectedProperty locn:postName  ;
    sh:minCount 1;
    sh:maxCount 1;
	sh:datatype xsd:string ;
	sh:severity sh:Violation ;
	sh:message "postName: The Address must have exactly one postName. This error may be due to not having a value, having more than one value, or having a value that is not a string."@en .

#thoroughfare
era-sh:AddressShape sh:property era-sh:Thoroughfare .
era-sh:Thoroughfare
	a sh:PropertyShape;
    era:affectedClass locn:Address ;
    era:affectedProperty locn:thoroughfare ;
    era:scope "local";
	rdfs:comment "The thoroughfare of a location "@en ;
	sh:path locn:thoroughfare  ;
	era:affectedProperty locn:thoroughfare  ;
    sh:minCount 1;
    sh:maxCount 1;
	sh:datatype xsd:string ;
	sh:severity sh:Violation ;
	sh:message "postName: The Address must have exactly one postName. This error may be due to not having a value, having more than one value, or having a value that is not a string."@en .

#fullAddress
era-sh:AddressShape sh:property era-sh:FullAddress .
era-sh:FullAddress
	a sh:PropertyShape;
    era:affectedClass locn:Address ;
    era:affectedProperty locn:fullAddress ;
    era:scope "local";
	rdfs:comment "The full address of a location as extracted"@en ;
	sh:path locn:fullAddress  ;
	era:affectedProperty locn:fullAddress  ;
    sh:minCount 0;
    sh:maxCount 1;
	sh:datatype xsd:string ;
	sh:severity sh:Warning ;
	sh:message "fullAddress: The Address should have one fullAddress. This error may be due to not having a value, having more than one value, or having a value that is not a string."@en .

era-sh:OrganisationRoleShape sh:property era-sh:NandoCode .
era-sh:NandoCode
    a sh:PropertyShape ;
    era:affectedClass era:OrganisationRole ;
    era:affectedProperty era:nandoCode ;
    era:scope "local" ;
    rdfs:comment "NANDO (New Approach Notified and Designated Organisations) code identifying the organisation that plays this role."@en ;
    sh:path era:nandoCode ;
    sh:datatype xsd:string ;
    sh:maxCount 1 ;
    sh:severity sh:Violation ;
    sh:message "nandoCode: An OrganisationRole must have at most one nandoCode. This error may be due to having more than one value or having a value that is not a string."@en .

# nandoCode applicability: only allowed for NOBO (notified body)
era-sh:OrganisationRoleShape sh:sparql era-sh:NandoCodeOnlyForNobo .
era-sh:NandoCodeOnlyForNobo
    a sh:SPARQLConstraint ;
    era:affectedClass era:OrganisationRole ;
    era:affectedProperty era:nandoCode ;
    era:scope "local" ;
    rdfs:comment "An OrganisationRole may only have a value for nandoCode if its hasOrganisationRole is the NOBO (notified body) concept <http://data.europa.eu/949/concepts/organisation-roles/NOBO>."@en ;
    sh:message "nandoCode: The OrganisationRole {$this} with label {?label} has a nandoCode {?value} but its hasOrganisationRole is {?role}, not <http://data.europa.eu/949/concepts/organisation-roles/NOBO>. A nandoCode is only allowed for notified bodies (NOBO)."@en ;
    sh:prefixes era: ;
    sh:select """
    PREFIX era: <http://data.europa.eu/949/>
    PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
    SELECT $this ?label ?value ?role (era:nandoCode AS ?path)
    WHERE {
        $this era:nandoCode ?value .
        OPTIONAL { $this rdfs:label ?label0 } .
        BIND(COALESCE(?label0, "unknown label") AS ?label)
        OPTIONAL { $this era:hasOrganisationRole ?role0 } .
        BIND(COALESCE(STR(?role0), "none") AS ?role)
        FILTER NOT EXISTS { $this era:hasOrganisationRole <http://data.europa.eu/949/concepts/organisation-roles/NOBO> }
    }
""" .

##############################################################################
# SHACL 1.2 Shapes for validating CLD validity periods
#
# Validates consistency between:
#   - dcterms:type        (CLD type: 1, 2, 4, 5, 6, 7)
#   - dcterms:conformsTo  (module: SB, SH1, CB, CH1, CD, CH, SD, SF, SG, CF, CV)
#   - dcterms:subject     (TSI legislation + amendments)
#   - dcterms:valid        → time:hasDurationDescription → time:years
#
# Relies on enrichment data loaded into the era-lex SPARQL endpoint at GRAPHDB:
#   repository:era-lex
#
# The enrichment data (data/era-lex-validity-rules.ttl) attaches
# era:cldValidityRule instances to each consolidated version of a TSI.
#
# The range of era:cldValidityRule is era:ValidityConstraint, with three
# disjoint subclasses:
#   era:MaxDurationConstraint        rdfs:subClassOf time:DurationDescription
#     → reuses time:years to express the maximum allowed duration
#   era:UnlimitedValidityConstraint
#     → no temporal bound
#   era:ForbiddenAssessmentConstraint
#     → type+module not permitted under this TSI
#
# Architecture:
#   Shape 1 — era:CLDForbiddenCombinationShape
#             Detects type+module combos flagged as ForbiddenAssessmentConstraint.
#   Shape 2 — era:CLDValidityDurationShape
#             Checks that the actual duration ≤ MaxDurationConstraint's time:years.
#   Shape 3 — era:CLDUnlimitedShouldNotExpireShape
#             Checks that UnlimitedValidityConstraint CLDs have no end/duration.
##############################################################################



##############################################################################
# Shape 1: Forbidden combination of CLD type + module for the given TSI
#
# Fires when the resolved constraint is an era:ForbiddenAssessmentConstraint.
##############################################################################

era-sh:CLDForbiddenCombinationShape
  a sh:NodeShape ;
  sh:targetClass era:CertificationLevelDocument;
  sh:nodeKind sh:IRI ;
  sh:severity sh:Violation ;
  sh:message "The combination of CLD type ({?cldType}) and module ({?cldModule}) is forbidden for the TSI ({?tsiRoot}) at the amendment level referenced by this CLD." ;
  sh:sparql era-sh:CLDForbiddenCombination .

era-sh:CLDForbiddenCombination
    a sh:SPARQLConstraint ;
    sh:select """
      PREFIX era: <http://data.europa.eu/949/>
      PREFIX dcterms: <http://purl.org/dc/terms/>
      PREFIX eli: <http://data.europa.eu/eli/ontology#>
      PREFIX owl: <http://www.w3.org/2002/07/owl#>

      SELECT $this ?cldType ?cldModule ?tsiRoot ?consolidated
      WHERE {
        # 1. Get CLD type and module
        $this dcterms:type       ?cldType .
        $this dcterms:conformsTo ?cldModule .

        # 2. Query era-lex for a ForbiddenAssessmentConstraint
        SERVICE <repository:era-lex> {
          ?tsiEli owl:sameAs ?tsiRoot .
          ?tsiEli eli:consolidated_by ?consolidated .

          ?consolidated era:cldValidityRule ?rule .
          ?rule a era:ForbiddenAssessmentConstraint .
          ?rule dcterms:type       ?cldType .
          ?rule dcterms:conformsTo ?cldModule .
        }

        # 3. CLD must reference the TSI root
        $this dcterms:subject ?tsiRoot .

        # 4. All amendments of this consolidated version must be covered
        FILTER NOT EXISTS {
          SERVICE <repository:era-lex> {
            ?consolidated eli:consolidates ?reqAmendment .
            FILTER(?reqAmendment != ?tsiEli)
          }
          FILTER NOT EXISTS {
            $this dcterms:subject ?reqAmendment .
          }
          FILTER NOT EXISTS {
            $this dcterms:subject ?cldAmendRef .
            SERVICE <repository:era-lex> {
              ?reqAmendment owl:sameAs ?cldAmendRef .
            }
          }
        }

        # 5. This must be the latest matching consolidated version
        FILTER NOT EXISTS {
          SERVICE <repository:era-lex> {
            ?tsiEli eli:consolidated_by ?newerConsolidated .
            ?newerConsolidated era:cldValidityRule ?newerRule .
            ?newerRule a era:ForbiddenAssessmentConstraint .
            ?newerRule dcterms:type       ?cldType .
            ?newerRule dcterms:conformsTo ?cldModule .
          }
          FILTER(?newerConsolidated != ?consolidated)

          FILTER NOT EXISTS {
            SERVICE <repository:era-lex> {
              ?newerConsolidated eli:consolidates ?reqAmendment2 .
              FILTER(?reqAmendment2 != ?tsiEli)
            }
            FILTER NOT EXISTS {
              $this dcterms:subject ?reqAmendment2 .
            }
            FILTER NOT EXISTS {
              $this dcterms:subject ?cldAmendRef2 .
              SERVICE <repository:era-lex> {
                ?reqAmendment2 owl:sameAs ?cldAmendRef2 .
              }
            }
          }

          SERVICE <repository:era-lex> {
            {
              SELECT ?newerConsolidated (COUNT(DISTINCT ?na) AS ?newerCount) WHERE {
                ?newerConsolidated eli:consolidates ?na .
              } GROUP BY ?newerConsolidated
            }
            {
              SELECT ?consolidated (COUNT(DISTINCT ?ca) AS ?currentCount) WHERE {
                ?consolidated eli:consolidates ?ca .
              } GROUP BY ?consolidated
            }
          }
          FILTER(?newerCount > ?currentCount)
        }
      }
    """
   .


##############################################################################
# Shape 2: CLD validity duration exceeds the allowed maximum
#
# Fires when the resolved constraint is an era:MaxDurationConstraint and the
# CLD's actual time:years exceeds the constraint's time:years.
# Falls back to Decision 2010/713/EU general rules if no TSI-specific
# constraint exists on any reachable consolidated version.
##############################################################################

era-sh:CLDValidityDurationShape
  a sh:NodeShape ;
  sh:targetClass era:CertificationLevelDocument;
  sh:nodeKind sh:IRI ;
  sh:severity sh:Violation ;
  sh:message "CLD validity duration ({?actualYears} years) exceeds the maximum allowed ({?maxYears} years) for type {?cldType} with module {?cldModule} under TSI {?tsiRoot}." ;
  sh:sparql era-sh:CLDValidityDuration .

era-sh:CLDValidityDuration
    a sh:SPARQLConstraint ;
    sh:select """
      PREFIX era: <http://data.europa.eu/949/>
      PREFIX dcterms: <http://purl.org/dc/terms/>
      PREFIX eli: <http://data.europa.eu/eli/ontology#>
      PREFIX owl: <http://www.w3.org/2002/07/owl#>
      PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
      PREFIX time: <http://www.w3.org/2006/time#>

      SELECT $this ?cldType ?cldModule ?tsiRoot ?maxYears ?actualYears
      WHERE {
        # 1. Get CLD properties
        $this dcterms:type       ?cldType .
        $this dcterms:conformsTo ?cldModule .
        $this dcterms:valid       ?validPeriod .
        ?validPeriod time:hasDurationDescription ?duration .
        ?duration time:years ?actualYearsRaw .
        BIND(xsd:decimal(?actualYearsRaw) AS ?actualYears)

        # 2. Get TSI subject
        $this dcterms:subject ?tsiRoot .

        # 3. Look up the applicable MaxDurationConstraint
        {
          # --- Path A: TSI-specific constraint ---
          SERVICE <repository:era-lex> {
            ?tsiEli owl:sameAs ?tsiRoot .
            ?tsiEli eli:consolidated_by ?consolidated .
            ?consolidated era:cldValidityRule ?rule .
            ?rule a era:MaxDurationConstraint .
            ?rule dcterms:type       ?cldType .
            ?rule dcterms:conformsTo ?cldModule .
            ?rule time:years ?maxYears .
          }

          # All amendments covered
          FILTER NOT EXISTS {
            SERVICE <repository:era-lex> {
              ?consolidated eli:consolidates ?reqAmendment .
              FILTER(?reqAmendment != ?tsiEli)
            }
            FILTER NOT EXISTS {
              $this dcterms:subject ?reqAmendment .
            }
            FILTER NOT EXISTS {
              $this dcterms:subject ?cldAmendRef .
              SERVICE <repository:era-lex> {
                ?reqAmendment owl:sameAs ?cldAmendRef .
              }
            }
          }

          # Latest matching consolidated version
          FILTER NOT EXISTS {
            SERVICE <repository:era-lex> {
              ?tsiEli eli:consolidated_by ?newerConsolidated .
              ?newerConsolidated era:cldValidityRule ?newerRule .
              ?newerRule a era:MaxDurationConstraint .
              ?newerRule dcterms:type       ?cldType .
              ?newerRule dcterms:conformsTo ?cldModule .
            }
            FILTER(?newerConsolidated != ?consolidated)
            FILTER NOT EXISTS {
              SERVICE <repository:era-lex> {
                ?newerConsolidated eli:consolidates ?reqAmendment2 .
                FILTER(?reqAmendment2 != ?tsiEli)
              }
              FILTER NOT EXISTS {
                $this dcterms:subject ?reqAmendment2 .
              }
              FILTER NOT EXISTS {
                $this dcterms:subject ?cldAmendRef2 .
                SERVICE <repository:era-lex> {
                  ?reqAmendment2 owl:sameAs ?cldAmendRef2 .
                }
              }
            }
            SERVICE <repository:era-lex> {
              {
                SELECT ?newerConsolidated (COUNT(DISTINCT ?na) AS ?newerCount) WHERE {
                  ?newerConsolidated eli:consolidates ?na .
                } GROUP BY ?newerConsolidated
              }
              {
                SELECT ?consolidated (COUNT(DISTINCT ?ca) AS ?currentCount) WHERE {
                  ?consolidated eli:consolidates ?ca .
                } GROUP BY ?consolidated
              }
            }
            FILTER(?newerCount > ?currentCount)
          }
        }
        UNION
        {
          # --- Path B: Fall back to Decision 2010/713/EU ---
          FILTER NOT EXISTS {
            SERVICE <repository:era-lex> {
              ?tsiEli2 owl:sameAs ?tsiRoot .
              ?tsiEli2 eli:consolidated_by ?anyConsolidated .
              ?anyConsolidated era:cldValidityRule ?anyRule .
              ?anyRule dcterms:type       ?cldType .
              ?anyRule dcterms:conformsTo ?cldModule .
            }
          }

          SERVICE <repository:era-lex> {
            <http://data.europa.eu/eli/dec/2010/713/oj> era:cldValidityRule ?genRule .
            ?genRule a era:MaxDurationConstraint .
            ?genRule dcterms:type       ?cldType .
            ?genRule dcterms:conformsTo ?cldModule .
            ?genRule time:years ?maxYears .
          }
        }

        # 4. Actual duration exceeds maximum
        FILTER(?actualYears > ?maxYears)
      }
    """
   .


##############################################################################
# Shape 3: Unlimited CLD should not have an end date or duration
#
# Fires when the resolved constraint is an era:UnlimitedValidityConstraint
# but the CLD's validity period has a time:hasEnd or time:hasDurationDescription.
##############################################################################

era-sh:CLDUnlimitedShouldNotExpireShape
  a sh:NodeShape ;
  sh:targetClass era:CertificationLevelDocument;
  sh:nodeKind sh:IRI ;
  sh:severity sh:Warning ;
  sh:message "CLD type {?cldType} with module {?cldModule} under TSI {?tsiRoot} should have unlimited validity, but an end date or duration is specified." ;
  sh:sparql era-sh:CLDUnlimitedShouldNotExpire .

era-sh:CLDUnlimitedShouldNotExpire
    a sh:SPARQLConstraint ;
    sh:select """
      PREFIX era: <http://data.europa.eu/949/>
      PREFIX dcterms: <http://purl.org/dc/terms/>
      PREFIX eli: <http://data.europa.eu/eli/ontology#>
      PREFIX owl: <http://www.w3.org/2002/07/owl#>
      PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
      PREFIX time: <http://www.w3.org/2006/time#>

      SELECT $this ?cldType ?cldModule ?tsiRoot
      WHERE {
        # 1. Get CLD properties
        $this dcterms:type       ?cldType .
        $this dcterms:conformsTo ?cldModule .
        $this dcterms:subject     ?tsiRoot .
        $this dcterms:valid       ?validPeriod .

        # 2. CLD has an end date or a duration — it should not
        {
          ?validPeriod time:hasEnd ?endDate .
        }
        UNION
        {
          ?validPeriod time:hasDurationDescription ?dur .
          ?dur time:years ?yrs .
        }

        # 3. Determine whether the applicable rule is UnlimitedValidityConstraint
        {
          # --- Path A: TSI-specific constraint ---
          SERVICE <repository:era-lex> {
            ?tsiEli owl:sameAs ?tsiRoot .
            ?tsiEli eli:consolidated_by ?consolidated .
            ?consolidated era:cldValidityRule ?rule .
            ?rule a era:UnlimitedValidityConstraint .
            ?rule dcterms:type       ?cldType .
            ?rule dcterms:conformsTo ?cldModule .
          }

          # All amendments covered
          FILTER NOT EXISTS {
            SERVICE <repository:era-lex> {
              ?consolidated eli:consolidates ?reqAmendment .
              FILTER(?reqAmendment != ?tsiEli)
            }
            FILTER NOT EXISTS {
              $this dcterms:subject ?reqAmendment .
            }
            FILTER NOT EXISTS {
              $this dcterms:subject ?cldAmendRef .
              SERVICE <repository:era-lex> {
                ?reqAmendment owl:sameAs ?cldAmendRef .
              }
            }
          }

          # Latest matching
          FILTER NOT EXISTS {
            SERVICE <repository:era-lex> {
              ?tsiEli eli:consolidated_by ?newerConsolidated .
              ?newerConsolidated era:cldValidityRule ?newerRule .
              ?newerRule dcterms:type       ?cldType .
              ?newerRule dcterms:conformsTo ?cldModule .
            }
            FILTER(?newerConsolidated != ?consolidated)
            FILTER NOT EXISTS {
              SERVICE <repository:era-lex> {
                ?newerConsolidated eli:consolidates ?reqAmendment2 .
                FILTER(?reqAmendment2 != ?tsiEli)
              }
              FILTER NOT EXISTS {
                $this dcterms:subject ?reqAmendment2 .
              }
              FILTER NOT EXISTS {
                $this dcterms:subject ?cldAmendRef2 .
                SERVICE <repository:era-lex> {
                  ?reqAmendment2 owl:sameAs ?cldAmendRef2 .
                }
              }
            }
            SERVICE <repository:era-lex> {
              {
                SELECT ?newerConsolidated (COUNT(DISTINCT ?na) AS ?newerCount) WHERE {
                  ?newerConsolidated eli:consolidates ?na .
                } GROUP BY ?newerConsolidated
              }
              {
                SELECT ?consolidated (COUNT(DISTINCT ?ca) AS ?currentCount) WHERE {
                  ?consolidated eli:consolidates ?ca .
                } GROUP BY ?consolidated
              }
            }
            FILTER(?newerCount > ?currentCount)
          }
        }
        UNION
        {
          # --- Path B: General rules from Decision 2010/713/EU ---
          FILTER NOT EXISTS {
            SERVICE <repository:era-lex> {
              ?tsiEli2 owl:sameAs ?tsiRoot .
              ?tsiEli2 eli:consolidated_by ?anyConsolidated .
              ?anyConsolidated era:cldValidityRule ?anyRule .
              ?anyRule dcterms:type       ?cldType .
              ?anyRule dcterms:conformsTo ?cldModule .
            }
          }

          SERVICE <repository:era-lex> {
            <http://data.europa.eu/eli/dec/2010/713/oj> era:cldValidityRule ?genRule .
            ?genRule a era:UnlimitedValidityConstraint .
            ?genRule dcterms:type       ?cldType .
            ?genRule dcterms:conformsTo ?cldModule .
          }
        }
      }
    """
   .


# ----------------------------------------------------------------------------
# Legal resource validity rules for CLD
# ----------------------------------------------------------------------------

era-sh:LegalResourceShape
    a sh:NodeShape ;
    sh:targetClass eli:LegalResource ;
    sh:nodeKind sh:IRI ;
    sh:property era-sh:CLDValidityRule .

era-sh:CLDValidityRule
    a sh:PropertyShape ;
    sh:path era:cldValidityRule  ;
    era:affectedProperty era:cldValidityRule  ;
    era:affectedProperty era:cldValidityRule ;
    era:affectedClass eli:LegalResource ;
    sh:name "CLD validity rule"@en ;
    sh:nodeKind sh:IRI ;
    sh:class era:ValidityConstraint ;
    sh:minCount 0 ;
    sh:description "Links a legal resource to a validity constraint governing a specific CLD type and module combination."@en ;
    sh:message "CLD validity rule must point to a validity constraint."@en ;
    sh:severity sh:Violation .


# ============================================================================
# ERADIS Validity Shapes
# ============================================================================
# This file defines SHACL shapes for modeling temporal validity periods using
# the W3C Time Ontology (https://www.w3.org/TR/owl-time/).
#
# Two main patterns are supported:
# 1. Start-End validity: A time interval with explicit beginning and end dates
# 2. Start-Duration validity: A time interval with a beginning date and duration
# ============================================================================

# ----------------------------------------------------------------------------
# Start-End Validity Pattern
# ----------------------------------------------------------------------------
# Used for entities with explicitly defined validity periods (start and end dates).
# Example use cases: Safety certificates, authorizations, operational permissions.


era-sh:StartEndValidityShape a sh:NodeShape ;
    sh:targetClass time:ProperInterval ;
    sh:nodeKind sh:IRI ;
    sh:property era-sh:IntervalBeginningDateShape ;
    sh:property era-sh:IntervalEndingDateShape .

# ----------------------------------------------------------------------------
# Start-Duration Validity Pattern
# ----------------------------------------------------------------------------
# Used for entities where validity is defined by a start date and a duration.
# Currently used for: CLD (Certification Level Document), Certificates.
# Note: Duration is typically expressed in years for certificate validity periods.
era-sh:StartDurationShape a sh:NodeShape ;
    sh:targetClass time:ProperInterval ;
    sh:nodeKind sh:IRI ;
    sh:property era-sh:IntervalBeginningDateShape ;
    # Duration is only managed for CLD, Certificate.
    sh:property era-sh:IntervalDurationShape .

# ----------------------------------------------------------------------------
# Duration Description
# ----------------------------------------------------------------------------
# Defines the duration of a validity interval using time:DurationDescription.
era-sh:IntervalDurationShape a sh:PropertyShape ;
    sh:path time:hasDurationDescription  ;
    era:affectedProperty time:hasDurationDescription  ;
    sh:name "Validity Duration" ;
    sh:description "Duration of the validity interval." ;
    sh:message "A validity interval with duration must have exactly one duration description."@en ;
    sh:severity sh:Violation ;
    sh:qualifiedValueShape era-sh:DurationLiteralShape ;
    sh:qualifiedMinCount 1 ; sh:qualifiedMaxCount 1 ;
    sh:minCount 1 ;
    sh:maxCount 1 .

# Validates the structure of a duration description (typically in years).
era-sh:DurationLiteralShape a sh:NodeShape ;
    sh:targetClass time:DurationDescription ;
    sh:nodeKind sh:IRI ;
    sh:property era-sh:DurationInYearsDescriptionShape .

# Specifies the duration value in years (e.g., 5 years for a certificate validity).
era-sh:DurationInYearsDescriptionShape a sh:PropertyShape ;
    sh:path time:years  ;
    era:affectedProperty time:years  ;
    sh:name "Duration in Years" ;
    sh:datatype xsd:nonNegativeInteger ;
    sh:description "The duration in years." ;
    sh:message "A validity interval with duration must have exactly one duration in years description."@en ;
    sh:severity sh:Violation ;
    sh:minCount 0 ;
    sh:maxCount 1 .

# ----------------------------------------------------------------------------
# Interval Beginning and End
# ----------------------------------------------------------------------------
# These shapes define the start and end points of a validity interval.
# Both beginning and end are represented as time:Instant with xsd:date values.

# Defines the start date of the validity period (mandatory).
era-sh:IntervalBeginningDateShape a sh:PropertyShape ;
    sh:path time:hasBeginning  ;
    era:affectedProperty time:hasBeginning  ;
    sh:name "Validity Start Date" ;
    sh:qualifiedValueShape era-sh:ValidityInstantShape ;
    sh:qualifiedMinCount 1 ; sh:qualifiedMaxCount 1 ;
    sh:description "Start date of the validity interval." ;
    sh:message "A validity interval must have exactly one start date."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 .

# Defines the end date of the validity period (mandatory).
era-sh:IntervalEndingDateShape a sh:PropertyShape ;
    sh:path time:hasEnd  ;
    era:affectedProperty time:hasEnd  ;
    sh:name "Validity End Date" ;
    sh:qualifiedValueShape era-sh:ValidityInstantShape ;
    sh:qualifiedMinCount 1 ; sh:qualifiedMaxCount 1 ;
    sh:description "End date of the validity interval." ;
    sh:message "A validity interval must have exactly one end date."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 .

# ----------------------------------------------------------------------------
# Time Instant Representation
# ----------------------------------------------------------------------------
# Defines how a specific point in time is represented using time:Instant.
# The instant must have exactly one xsd:date value.

# Validates the structure of a time instant.
era-sh:ValidityInstantShape a sh:NodeShape ;
    sh:targetClass time:Instant ;
    sh:nodeKind sh:IRI ;
    sh:property era-sh:ValidityInstantXSDDateShape .

# Ensures the instant has a valid xsd:date literal (YYYY-MM-DD format).
era-sh:ValidityInstantXSDDateShape a sh:PropertyShape ;
    sh:path time:inXSDDate  ;
    era:affectedProperty time:inXSDDate  ;
    sh:name "Validity Instant (XSD Date)" ;
    sh:datatype xsd:date ;
    sh:description "The instant in time represented as an XSD Date." ;
    sh:message "A time instant must have exactly one XSD date value."@en ;
    sh:severity sh:Violation ;
    sh:minCount 1 ;
    sh:maxCount 1 .

