Rechercher l’API de disponibilité des ressources

Les organisations Field Service doivent planifier le travail, souvent par le biais d’un agent de service ou directement par le client. Les réservations sont généralement créées sur la base des ressources disponibles pour l’entreprise et les exigences du travail.

Lorsque vous utilisez au moins Dynamics 365 Field Service v8.8.43.51 et Universal Resource Scheduling v3.12.46.21 pour planifier le travail, utilisez l’API msdyn_SearchResourceAvailability pour récupérer toutes les ressources éligibles à la tâche, afin de pouvoir planifier efficacement le travail. Au moment où j’écris ces lignes, la version 3 est la dernière version et msdyn_SearchResourceAvailability prend en charge les appels web API.

Note

Utilisez la dernière version de l’API car les anciennes versions peuvent utiliser des méthodes d’authentification obsolètes.

Paramètres d’entrée

Nom  Type Description Requise Default
Version String Le numéro de version de l’API identifie la version de l’API qui doit être appelée. Il suit le format de major.minor.patch. La demande ne doit pas nécessairement contenir le numéro de version complet.

  • Si seule une version majeure est spécifiée, elle appelle la version mineure et de correctif la plus élevée disponible pour cette version majeure.
  • Si les versions majeure et mineure sont spécifiées, elle appelle la version de correctif la plus élevée disponible.
  • Si les trois parties de la version sont mentionnées, elle invoque la version exacte de l’API spécifiée.
  • Oui -S.O.-
    IsWebApi Valeur booléenne Réglez sur True pour utiliser l’assistant de planification via l’API web. Oui -S.O.-
    Exigence Entity Cet attribut spécifie le besoin en ressources pour lequel la disponibilité des ressources est extraite. Cela devrait être une entité de type msdyn_resourcerequirement. L’exigence peut être un enregistrement préexistant de la base de données, ou un enregistrement créé à la volée avec les contraintes nécessaires. L’entité doit contenir tous les détails pertinents pour votre recherche. Le @odata.type pour cette entité doit être Microsoft.Dynamics.CRM.msdyn_requirement. Voici quelques attributs importants à remplir :
    1. msdyn_fromdate (DateHeure) : Exigence à partir de la date au format ISO
    2. msdyn_todate (DateHeure) : Exigence à la date au format ISO
    3. msdyn_remainingduration (Entier) : La durée restante de l’exigence en minutes
    4. msdyn_duration (Entier) : La durée totale de l’exigence en minutes
    Oui -S.O.-
    Paramètres Entity L’attribut Paramètres permet de filtrer davantage les ressources récupérées. Spécifie les paramètres comme des attributs dans un sac d’entité. Le type d’entité n’a pas d’importance. Vous pouvez spécifier n’importe quel nom logique d’entité. Oui -S.O.-
    ResourceSpecification Entity Définissez l’attribut resourceSpecification comme des attributs dans un sac d’entité. Le @odata.type pour cette entité doit être Microsoft.Dynamics.CRM.expando. No None

    Entité Paramètres

    L’entité Paramètres n’est pas une entité qui existe dans Dataverse ; cependant, c’est une collection de tous les attributs suivants qui aide l’API de l’assistant de planification à filtrer les résultats. Ainsi, le @odata.type pour cette entité doit être Microsoft.Dynamics.CRM.expando.

    Nom  Type Description Requise Default
    ConsiderSlotsWithLessThanRequiredCapacity Valeur booléenne Définissez ceci sur True si un créneau horaire avec moins que la capacité requise (effort) doit être pris en compte lors du calcul des créneaux horaires disponibles potentiels sur le calendrier de la ressource. No Faux
    ConsiderSlotsWithLessThanRequiredDuration Valeur booléenne Définissez ceci sur True si un créneau horaire avec moins que la durée requise doit être pris en compte lors du calcul des créneaux horaires disponibles potentiels sur le calendrier de la ressource. No Faux
    ConsiderSlotsWithOverlappingBooking Valeur booléenne Définissez ceci sur True si un créneau horaire avec des réservations qui se chevauchent doit être pris en compte lors du calcul des créneaux horaires disponibles potentiels sur le calendrier de la ressource. No Faux
    ConsiderSlotsWithProposedBookings Valeur booléenne Définissez ceci sur True si un créneau horaire avec des réservations proposées doit être pris en compte lors du calcul des créneaux horaires disponibles potentiels sur le calendrier de la ressource. No Faux
    ConsiderAppointments Valeur booléenne Définissez cette valeur sur True pour que l’API de recherche de disponibilité des ressources respecte les rendez-vous Dataverse comme des réservations sur la ressource, à condition que les paramètres au niveau de l’organisation et des ressources aient été définis . Les rendez-vous avec les statuts Occupé ou Terminé sont considérés comme indisponibles pour les opérations de planification. No Faux
    ConsiderTravelTime Valeur booléenne Définissez ceci sur True si un temps de trajet doit être pris en compte lors du calcul des créneaux horaires potentiels sur le calendrier de la ressource. No Vrai
    ExcludeResourceCharacteristics Valeur booléenne Réglez cela sur Vrai pour exclure les caractéristiques des ressources des créneaux horaires en réponse. No Faux
    MovePastStartDateToCurrentDate Valeur booléenne Définissez ceci sur True pour déplacer une date de début dans le passé à la date actuelle. No Faux
    UseRealTimeResourceLocation Valeur booléenne Définissez ceci sur True si l’emplacement en temps réel des ressources doit être utilisé lors du calcul des créneaux horaires potentiels sur le calendrier de la ressource. No Faux
    SortOrder EntityCollection Spécifiez l’ordre de tri à l’aide d’une collection d’entités. Chaque entité de la collection représente un critère de tri et ne peut trier qu’à Resources partir de la réponse, mais pas TimeSlotsà partir de . Le @odata.type pour cette entité doit être Microsoft.Dynamics.CRM.expando. Voici les attributs que vous devez remplir :
    1. Name (Chaîne) : Les critères de tri
    2. SortOrder (Entier) : Le sens du tri (0 pour l’ordre croissant et 1 pour l’ordre décroissant)
    No None
    MaxResourceTravelRadius Entity Cet attribut spécifie la valeur maximum pouvant être définie dans une entité. Le @odata.type pour cette entité doit être Microsoft.Dynamics.CRM.expando. Voici les attributs que vous devez remplir :
    1. Value (Decimal) : Le rayon
    2. Unit (Entier) : L’unité de distance. Voir le groupe d’options d’unité msdyn_distance pour les valeurs possibles.
    No 0 km. Si tel est le cas, aucune ressource n’est renvoyée pour les besoins sur site.
    MaxNumberOfResourcesToEvaluate Integer Cet attribut définit une limite sur le nombre de ressources prises en compte pour la demande. No Si cet attribut n’est pas inclus dans l’appel API, le système utilise la Limite de Récupération de Disponibilité des Ressources à partir de la définition d’entité schedulable telle que définie dans les paramètres d’édition pour les entités activées. S’il est inclus dans l’appel, il remplace la limite d’extraction de disponibilité des ressources définie.
    ConsiderOutlookSchedules Valeur booléenne Réglez cela sur Vrai si les plannings de Outlook doivent être pris en compte. Disponible uniquement dans les versions 3.1.0 et ultérieures. No Faux

    Entité de spécification de ressources

    Nom  Type Description Requise Default
    ResourceTypes EntityCollection Cet attribut spécifie le type de ressource requis pour l’exigence. Utilisez une collection d’entités pour spécifier cet attribut. Chaque entité de la collection représente un type de ressource réservable. Le @odata.type pour cette entité doit être Microsoft.Dynamics.CRM.msdyn_resourceType. Cette caractéristique est requise :
    1. Value (Entier) : La valeur de groupe d’options qui représente le type de ressource :
      • 1- Générique
      • 2- Contact
      • 3- Utilisateur
      • 4- Équipement
      • 5- Compte
      • 6- Équipe
      • 7- Installation
      • 8- Groupes
    No Tous les types de ressources sauf les équipes
    PreferredResources EntityCollection Cet attribut spécifie les ressources préférées pour l’exigence. Ajoutez des ressources à cette collection d’entités pour vous assurer qu’elles figurent en haut de la liste des ressources disponibles. Même les ressources qui ne font pas partie de la collection d’entités sont sur la liste, mais seulement après les ressources préférées. No None
    RestrictedResources EntityCollection Cet attribut spécifie les ressources qui ne doivent pas être prises en compte pour l’exigence. Tous les créneaux temporels de cette ressource sont filtrés de la liste des résultats de cette API. No None
    MustChooseFromResources EntityCollection Cet attribut spécifie les seules ressources pouvant figurer sur la liste des ressources disponibles. Il filtre tous les autres résultats de la liste de sortie.
    Contraintes Entity Cet attribut spécifie les contraintes supplémentaires qui doivent être appliquées à la récupération des ressources disponibles. No None
    RetrieveResourcesQueryId Guid L’ID de la requête Récupérer les ressources. No ID de la requête Récupérer les ressources par défaut.
    BookedResourceId Guid Cet attribut spécifie la ressource actuellement réservée pour l’exigence. No None

    Note

    Utilisez une collection d’entités de ressources réservables pour spécifier les attributs de ressources Préférentiels, Restreintes et MustChooseFrom . Chaque entité de la collection représente une ressource Preferred, Restricted ou MustChooseFrom . Cette caractéristique est requise pour eux :

    1. Valeur (Guid) : L’identifiant de ressource réservable de la ressource Préférée, Restreinte ou MustChooseFrom . Le @odata.type pour cette entité doit être Microsoft.Dynamics.CRM.msdyn_bookableresource.

    Contraintes

    Spécifiez des contraintes supplémentaires via des attributs dans cette entité. Le type d’entité n’a pas d’importance. Vous pouvez spécifier n’importe quel nom logique d’entité.

    Passez en revue la requête Récupérer les ressources dans les paramètres du tableau de planification pour identifier les contraintes susceptibles de s’appliquer. Par défaut, cela inclut ce qui suit :

    Nom  Type Description
    Caractéristiques EntityCollection Un ensemble de caractéristiques qu’une ressource qualifiée doit posséder. Chaque entrée contient un characteristic avec l’identifiant caractéristique. En option, incluez un ratingvalue avec l’ID de valeur de notation pour filtrer les ressources par un niveau de compétence spécifique.
    Rôles EntityCollection Une collection d’ID de rôle qu’une ressource qualifiée doit avoir.
    Secteurs de vente EntityCollection Une collection d’identifiants de territoire. Une ressource qualifiée doit être affectée à l’un des territoires.
    UnspecifiedTerritory Valeur booléenne En combinaison avec la contrainte territoires, spécifie qu’une ressource qualifiéee doit être affecté à l’un des territoires ou à aucun territoire du tout.
    OrganizationalUnits EntityCollection Un ensemble d’ID d’unité d’organisation. Une ressource qualifiée doit être membre de l’une des unités organisationnelles spécifiées.
    Teams EntityCollection Une collection d’identifiants d’équipe. Une ressource qualifiée doit appartenir à l’une des équipes (implique que le type de ressource est un utilisateur système).
    BusinessUnits EntityCollection Une collection d’identifiants d’unités commerciales. Une ressource qualifiée doit appartenir à l’une des divisions (implique que le type de ressource est un utilisateur système).

    Paramètres de sortie

    Au niveau le plus élevé, la sortie a les quatre paramètres suivants. Les résultats sont représentés dans des collections d’entités et des entités. Les réponses peuvent ne pas inclure tous les attributs décrits ici, car les valeurs nulles ou non applicables sont omises de la réponse. Vérifiez toujours la présence d’un attribut avant d’essayer d’y accéder.

    Nom  Type Description
    TimeSlots EntityCollection Collection des résultats de plages horaires. Pour plus d’informations, consultez la section Entité de créneau horaire .
    Resources EntityCollection Collection des résultats de ressource. Les ressources sont représentées comme une collection d’entités avec les attributs suivants :
    1. BookableResource (Entité) : L’entité de ressource réservable qui est disponible pour l’exigence.
    2. TotalAvailableTime (Double) : le temps total disponible pour la ressource pour exécuter l’exigence.
    Connexe Entity Les ressources associées représentent les ressources et les créneaux horaires des ressources qui ne sont pas directement qualifiées pour l’exigence demandée, mais qui sont associées. Par exemple, si un membre de l’équipe se qualifie pour une exigence, alors les autres membres de cette équipe seraient des résultats liés.
    1. Timeslots (EntityCollection) : Plages horaires des ressources associées. La définition des plages horaires est la même que celle décrite dans la section plages horaires.
    2. Resources (EntityCollection) : Les ressources associées. La définition des ressources est la même que celle décrite dans la définition de l’attribut des ressources.
    Exceptions Entity Cet attribut contient des informations sur toute exception qui s’est produite et des informations sur si et où la recherche de ressources a été tronquée.
    1. Message (Chaîne) : Message d’exception
    2. ResourcesTruncatedAt (Entier) : si le nombre de ressources a dépassé la limite de récupération ; le nombre où les ressources ont été tronquées.

    Entité des plages horaires

    Nom  Type Description
    ID Guid Identificateur unique de la plage horaire
    Type Integer Le type de créneau horaire. Il peut s’agir de l’une des valeurs suivantes :
    • 0 : Disponible
    • 1 : Planifiée
    • 2 : Désactivée
    • 3 : Pause
    StartTime DateHeure Heure de début de la plage horaire. S’il y a des déplacements pour l’exigence, c’est à ce moment que le voyage commence à commencer. Sinon, c’est à ce moment que commence l’exigence.
    ArrivalTime DateHeure Heure de fin de la plage horaire. S’il y a des déplacements pour l’exigence, ce moment correspond au début de l’exigence, après la fin du voyage. Sinon, c’est la même que l’heure de début de la plage horaire.
    EndTime DateHeure Heure de fin de la plage horaire.
    Effort Integer L’effort ou la capacité de la ressource à réaliser les exigences.
    ResourceRequirement EntityReference Le besoin en ressources pour lequel les créneaux horaires sont récupérés.
    Potential Valeur booléenne Une valeur booléenne indiquant si l’intervalle de temps a le potentiel de répondre à l’exigence demandée.
    IsDuplicate Valeur booléenne Valeur booléenne indiquant si le créneau est un doublon.
    AllowOverlapping Valeur booléenne Valeur booléenne indiquant si le chevauchement est autorisé.
    Ressource Entity Resource à laquelle appartient la plage horaire. Pour en savoir plus, voir ressource de la plage horaire.
    Emplacement Entity L’emplacement a trois attributs :
    1. Location (Entité) : Il a deux attributs -
      • Latitude
      • Longitude
    2. WorkLocation (Entier) : Il a trois attributs -
      • Sur site. Les exigences sur site excluent les types de ressources de groupe et d’installation des résultats.
      • Installation
      • Emplacement non spécifié
    3. LocationSourceSlot (Entier) : La source des informations de localisation a trois attributs -
      • Commun
      • Entité GPS personnalisée
      • Audit mobile
    Déplacement Entity Cette entité contient des détails de temps de trajet et des informations de distance pour une plage horaire. Ci-après les attributs :
    1. Distance (Double) : La distance parcourue
    2. TravelTime (Double) : Le temps de trajet en minutes.
    3. DistanceFromStartLocation (Double) : La distance depuis l’emplacement de départ de la ressource.
    4. DistanceFromEndLocation (Double) : La distance depuis l’emplacement de fin de la ressource.
    5. DistanceMethodSourceSlot (entier) : La source ou le type de calcul des valeurs de distance
      • Service Map
      • À vol d’oiseau
    Suivant Entity Cette entité contient des détails sur le temps de trajet et la distance jusqu’à la prochaine réservation de la plage horaire.
    1. NextScheduleLocation (Entité) : Le lieu de la prochaine réservation. L’entité a deux attributs :
      • Latitude
      • Longitude
    2. NextScheduleTravelTime (Entier) : Le temps de trajet jusqu’à la prochaine réservation en minutes.
    Disponibilité Entity Les informations de disponibilité détaillées pour une plage horaire. Cette entité est utilisée avec les groupes temporels.
    1. AvailableIntervals (EntityCollection) : Une collection d’intervalles disponibles. Chaque entité de cette collection contient des détails sur un intervalle de groupe de temps.
      • StartTime (DateHeure) : L’heure de début.
      • ArrivalTime (DateHeure) : L’heure d’arrivée.
      • EndTime (DateHeure) : L’heure de fin.
      • TimeGroupId (DateHeure) : L’ID du groupe de temps.
      • TimeGroupDetailStartTime (DateHeure) : L’heure de début du groupe horaire.
      • TimeGroupDetailEndTime (DateHeure) : L’heure de fin du groupe horaire.
    2. TotalAvailableDuration (Double) : La durée totale disponible en minutes.
    3. TotalAvailableTime (Double) : le temps total de disponibilité d’une ressource au cours d’une journée (en minutes).
    TimeGroup Entity Les détails sur un groupe de temps.
    1. TimeGroupId (Guid) : L’ID du groupe de temps.
    2. TimeGroupDetail (EntityReference) : Une référence d’entité au détail du groupe horaire.
    3. TimeGroupDetailStartTime (DateHeure) : L’heure de début détaillée du groupe horaire.
    4. TimeGroupDetailEndTime (DateHeure) : L’heure de fin détaillée du groupe horaire.

    Astuce

    Lorsque vous créez des réservations en utilisant l’API, utilisez le champ Potentiel décrit dans le tableau. Si vous n’utilisez pas ce champ, vous pourriez obtenir des réservations qui se chevauchent ou inadaptées.

    Ressource de la plage horaire

    Nom  Type Description
    Ressource EntityReference Référence d’entité à la ressource réservable.
    Groupe de ressources EntityReference Référence d’entité au groupe de ressources réservables.
    BusinessUnit EntityReference Référence d’identité à la division.
    OrganizationalUnit EntityReference Référence d’entité à l’unité d’organisation.
    ResourceType Integer Le type de ressource. Voir l’attribut ResourceType sur l’entité BookableResource pour les valeurs possibles.
    PoolId Guid ID du groupe auquel la ressource appartient pour la durée du créneau.
    CrewId Guid ID de l’équipe auquel la ressource appartient pour la durée du créneau.
    Caractéristiques EntityCollection Caractéristiques des ressources pouvant être réservées. Chaque entité de la collection contient des entités avec des caractéristiques et des informations de notation.
    1. Characteristic (EntityReference) : Une référence d’entité à la caractéristique.
    2. RatingId (Guid) L’ID d’évaluation pour la caractéristique.
    3. RatingName (Chaîne) : Le nom de l’évaluation.
    4. RatingValue (Entier) : La valeur d’évaluation.
    HasStartLocation Valeur booléenne Valeur booléenne indiquant si la ressource a un emplacement de départ.
    HasEndLocation Valeur booléenne Valeur booléenne indiquant si la ressource a un emplacement de fin.
    Adresse e-mail String Adresse e-mail de la ressource.
    Téléphone String Numéro de téléphone de la ressource.
    ImagePath String Chemin d’accès à l’image de la ressource.
    CalendarId Guid ID d’agenda de la ressource.

    Examples

    Dans cet exemple, vous utilisez la version 3 de l’API assistant de planification, qui prend en charge les appels web API, pour une exigence d’une durée de 60 minutes. En utilisant l’attribut settings , vous filtrez les résultats. Vous considérez deux types de ressources pour les résultats finaux : 1 et 2 (en d’autres termes, générique et contact).

    {
        "Version": "4",
        "IsWebApi": true,
        "Requirement": {
            "msdyn_fromdate": "2021-07-14T00:00:00Z",
            "msdyn_todate": "2021-07-15T23:59:00Z",
            "msdyn_remainingduration": 60,
            "msdyn_duration": 60,
            "msdyn_TimeGroup@odata.bind": "/msdyn_timegroups(c3dc79ea-d12f-ee11-9cc9-000d3a745a58)",
            "@odata.type": "Microsoft.Dynamics.CRM.msdyn_resourcerequirement"
        },
        "Settings": {
            "ConsiderSlotsWithProposedBookings": false,
            "MovePastStartDateToCurrentDate": true,
            "@odata.type": "Microsoft.Dynamics.CRM.expando"
        },
        "ResourceSpecification": {
            "@odata.type": "Microsoft.Dynamics.CRM.expando",
            "ResourceTypes@odata.type": "Collection(Microsoft.Dynamics.CRM.expando)",
            "ResourceTypes": [
                {
                    "@odata.type": "Microsoft.Dynamics.CRM.expando",
                    "value": "1"
                },
                {
                    "@odata.type": "Microsoft.Dynamics.CRM.expando",
                    "value": "2"
                }
            ],
            "Constraints": {
                "@odata.type": "Microsoft.Dynamics.CRM.expando",
                "Characteristics@odata.type": "Collection(Microsoft.Dynamics.CRM.expando)",
                "Characteristics": [
                    {
                        "@odata.type": "Microsoft.Dynamics.CRM.expando",
                        "characteristic": {
                            "@odata.type": "Microsoft.Dynamics.CRM.expando",
                            "value": "67387f9f-12e2-ec11-bb43-000d3aed25f7"
                        },
                        "ratingvalue": {
                            "@odata.type": "Microsoft.Dynamics.CRM.expando",
                            "value": "a1b2c3d4-5678-90ab-cdef-1234567890ab"
                        }
                    }
                ],
                "Territories@odata.type": "Collection(Microsoft.Dynamics.CRM.expando)",
                "Territories": [
                    {
                        "@odata.type": "Microsoft.Dynamics.CRM.expando",
                        "value": "cc19f004-4483-ee11-8178-000d3a5c32c3"
                    }
                ],
                "Roles@odata.type": "Collection(Microsoft.Dynamics.CRM.expando)",
                "Roles": [
                    {
                        "@odata.type": "Microsoft.Dynamics.CRM.expando",
                        "value": "76998e42-744c-f011-877d-6045bdfb899e"
                    }
                ]
            }
        }
    }
    
    

    L’exemple suivant montre l’utilisation appropriée des collections d’entités. Dans ce cas, elle spécifie MustChooseFromResources.

    {
        "Version": "4",
        "IsWebApi": true,
        "Requirement": {
            "msdyn_fromdate": "2021-07-14T00:00:00Z",
            "msdyn_todate": "2021-07-15T23:59:00Z",
            "msdyn_remainingduration": 60,
            "msdyn_duration": 60,
            "msdyn_latitude": 47.64807,
            "msdyn_longitude": -122.41249,
            "msdyn_worklocation": 690970000,
            "msdyn_TimeGroup@odata.bind": "/msdyn_timegroups(c3dc79ea-d12f-ee11-9cc9-000d3a745a58)",
            "@odata.type": "Microsoft.Dynamics.CRM.msdyn_resourcerequirement"
        },
        "Settings": {
            "ConsiderSlotsWithProposedBookings": false,
            "MovePastStartDateToCurrentDate": true,
            "MaxNumberOfResourcesToEvaluate":500,
            "ConsiderTravelTime": true,
            "MaxResourceTravelRadius": {
                "Value": 20,
                "Unit" : 192350000,
                "@odata.type": "Microsoft.Dynamics.CRM.expando"
            },
            "@odata.type": "Microsoft.Dynamics.CRM.expando"
        },
        "ResourceSpecification": {
            "@odata.type": "Microsoft.Dynamics.CRM.expando",
            "ResourceTypes@odata.type": "Collection(Microsoft.Dynamics.CRM.expando)",
            "ResourceTypes": [
                {
                    "@odata.type": "Microsoft.Dynamics.CRM.expando",
                    "value": "1"
                },
                {
                    "@odata.type": "Microsoft.Dynamics.CRM.expando",
                    "value": "2"
                }
            ],
            "MustChooseFromResources@odata.type": "Collection(Microsoft.Dynamics.CRM.expando)",
            "MustChooseFromResources": [
                {
                    "@odata.type": "Microsoft.Dynamics.CRM.expando",
                    "value": "2145a982-f718-ed11-b83e-0022482d79c8"
                }
            ],
            "Constraints": {
                "@odata.type": "Microsoft.Dynamics.CRM.expando",
                "Characteristics@odata.type": "Collection(Microsoft.Dynamics.CRM.expando)",
                "Characteristics": [
                    {
                        "@odata.type": "Microsoft.Dynamics.CRM.expando",
                        "characteristic": {
                            "@odata.type": "Microsoft.Dynamics.CRM.expando",
                            "value": "67387f9f-12e2-ec11-bb43-000d3aed25f7"
                        },
                        "ratingvalue": {
                            "@odata.type": "Microsoft.Dynamics.CRM.expando",
                            "value": "a1b2c3d4-5678-90ab-cdef-1234567890ab"
                        }
                    }
                ],
                "Territories@odata.type": "Collection(Microsoft.Dynamics.CRM.expando)",
                "Territories": [
                    {
                        "@odata.type": "Microsoft.Dynamics.CRM.expando",
                        "value": "cc19f004-4483-ee11-8178-000d3a5c32c3"
                    }
                ]
            }
        }
    }