ControlAddIn "Calendar V2 UTCAP"
Met à disposition un calendrier pour la planification de tâches (vue Planning d'équipe).
Remarks
Modèle de positionnement
Le calendrier se structure en une grille de PeriodCount périodes (colonnes principales)
× SubPeriodCount sous-périodes, soit PeriodCount × SubPeriodCount colonnes au total.
Tâches, marqueurs et périodes fermées se placent uniquement par index de colonne
(startIndex/endIndex, 0-based, bornes inclusives) — jamais par date ou heure.
La conversion est : index de colonne = indexPériode × SubPeriodCount + indexSousPériode.
Le codeunit Calendar Column Math UTCAP fournit ToColumnIndex et les procédures inverses.
SetRenderer est le point d'entrée : chaque appel réinitialise le planning (ressources,
tâches, marqueurs, périodes fermées et tooltips sont vidés) puis applique la nouvelle configuration.
Examples
L'exemple ci-dessous affiche un planning d'équipe sur 5 jours × 2 demi-journées (Matin / Après-midi). Pour créer une tâche, double-cliquez sur une cellule vide du calendrier. Vous pouvez déplacer et redimensionner les tâches éditables par glisser-déposer. Un clic droit sur une tâche propose de la supprimer via un menu contextuel.
namespace CapVision.Samples;
using Microsoft.Projects.Resources.Resource;
using System.Utilities;
using CapVision.Core.Controls;
page 50100 "Test Calendar UTCAP"
{
PageType = Card;
ApplicationArea = All;
UsageCategory = Tasks;
Caption = 'Test Calendar', Locked = true;
Extensible = false;
layout
{
area(Content)
{
usercontrol(Calendar; "Calendar V2 UTCAP")
{
trigger OnControlAddInReady()
begin
LoadCalendar();
end;
trigger OnCalendarContextMenuRequested(assignedTo: Text; index: Integer)
var
Helper: Codeunit "Calendar Helper V2 UTCAP";
ContextMenuItems: JsonArray;
begin
ContextMenuItems.Add(Helper.CreateMenuItem('newTask', 'Créer une tâche', 'New'));
CurrPage.Calendar.OpenCalendarContextMenu(assignedTo, index, ContextMenuItems);
end;
trigger OnCalendarContextMenuClicked(assignedTo: Text; index: Integer; contextMenuItemId: Text)
begin
if contextMenuItemId = 'newTask' then
CreateSampleTask(assignedTo, index);
end;
trigger OnCalendarDoubleClicked(assignedTo: Text; index: Integer)
begin
CreateSampleTask(assignedTo, index);
end;
trigger OnTaskContextMenuRequested(taskId: Text)
var
Helper: Codeunit "Calendar Helper V2 UTCAP";
ContextMenuItems: JsonArray;
begin
ContextMenuItems.Add(Helper.CreateMenuItem('deleteTask', 'Supprimer cette tâche', 'Delete'));
CurrPage.Calendar.OpenTaskContextMenu(taskId, ContextMenuItems);
end;
trigger OnTaskContextMenuClicked(taskId: Text; contextMenuItemId: Text)
begin
if contextMenuItemId = 'deleteTask' then
CurrPage.Calendar.DeleteTask(taskId);
end;
trigger OnTaskMoved(taskId: Text; newAssignedTo: Text; newStartIndex: Integer; newEndIndex: Integer)
begin
Message('Task %1 moved to resource %2, columns %3..%4', taskId, newAssignedTo, newStartIndex, newEndIndex);
end;
trigger OnTaskClicked(taskId: Text)
begin
Message('Task clicked: %1', taskId);
end;
trigger OnResourceClicked(resourceId: Text)
begin
Message('Resource clicked: %1', resourceId);
end;
}
}
}
local procedure LoadCalendar()
var
Resource: Record Resource;
Helper: Codeunit "Calendar Helper V2 UTCAP";
ResourceImage: Codeunit "Temp Blob";
ResourceImageInStream: InStream;
ResourceImageOutStream: OutStream;
Renderer: JsonObject;
Resources: JsonArray;
Periods, SubPeriods : List of [Text];
begin
// 1. Configure l'affichage (réinitialise le planning).
Periods.Add('Lundi'); Periods.Add('Mardi'); Periods.Add('Mercredi'); Periods.Add('Jeudi'); Periods.Add('Vendredi');
SubPeriods.Add('Matin'); SubPeriods.Add('Après-midi');
Renderer := Helper.CreateTeamPlanRenderer(5, Periods, 2, SubPeriods);
Helper.SetHeaderColumnWidth(Renderer, 220);
Helper.SetCollapsedHeaderMaxVisibleRows(Renderer, 2);
Helper.SetCornerCellText(Renderer, 'Équipe');
Helper.SetSubPeriodWidth(Renderer, '*');
CurrPage.Calendar.SetRenderer(Renderer);
// 2. Publie les ressources.
if Resource.FindSet() then
repeat
Clear(ResourceImage);
if Resource.Image.HasValue() then begin
ResourceImage.CreateOutStream(ResourceImageOutStream);
Resource.Image.ExportStream(ResourceImageOutStream);
end;
ResourceImage.CreateInStream(ResourceImageInStream);
Resources.Add(Helper.CreateResource(Resource."No.", Resource.Name, Resource."Job Title", '', ResourceImageInStream));
until Resource.Next() = 0;
CurrPage.Calendar.CreateResources(Resources);
end;
local procedure CreateSampleTask(ResourceId: Text; ColumnIndex: Integer)
var
Helper: Codeunit "Calendar Helper V2 UTCAP";
begin
// La tâche est posée dans la seule colonne cliquée (startIndex = endIndex = ColumnIndex).
CurrPage.Calendar.CreateTask(
Helper.CreateTask(Format(CreateGuid()), ResourceId, ColumnIndex, ColumnIndex, 'Réunion', 'Salle 1', 'Bâtiment C', 2, true, '#21888c', '#e74c3c'));
end;
}
Methods
SetRenderer
Définit l'affichage à utiliser. Chaque appel réinitialise le planning (ressources,
tâches, marqueurs, périodes fermées et tooltips sont vidés) puis applique la configuration.
C'est le point d'entrée : appelez-le en réponse à OnControlAddInReady, puis republiez vos données.
procedure SetRenderer(config: JsonObject)
Parameters
| Name | Type | Description |
|---|---|---|
| config | JsonObject |
Configuration du renderer (voir |
Examples
Configuration de la vue Planning d'équipe (Team) :
{
"renderer": 2, // 2 = Team (obligatoire pour afficher la vue)
"options": {
"Locale": "fr-FR", // Locale d'affichage, format "xx-XX"
"PeriodCount": 5, // Nombre de colonnes principales (fait foi)
"Periods": ["Lundi","Mardi","Mercredi","Jeudi","Vendredi"], // Libellés par index de période
"SubPeriodCount": 2, // Nombre de sous-colonnes par période (fait foi ≥ 1, sert au calcul d'index)
"SubPeriods": ["Matin","Après-midi"], // Libellés par index. Plus courte → colonnes de fin vides ; plus longue → surplus ignoré. Liste vide (ou tous vides) → colonnes conservées mais ligne des sous-périodes masquée
"BreakSubPeriods": [1], // (opt) Index de sous-colonnes de pause (hachurées, non-droppables)
"SubPeriodsMode": "default", // (opt) "default" (défaut) = alignés par index, sans bouclage (consécutifs identiques fusionnés) | "repeat" = bouclés modulo pour remplir la période (["A","B"]×6 → A B A B A B) | "stretch" = espacés : chaque libellé suivi de ⌈SubPeriodCount÷nbLibellés⌉−1 vides + vides finaux éventuels (4 lbl/10 → X··X··X··X ; /12 → X··X··X··X··). Mutuellement exclusifs. Voir Helper.SetSubPeriodsMode
"CollapsedHeaderMaxVisibleRows": 2, // (opt) Sous-lignes visibles avant repli
"RowHeight": "50", // (opt) Hauteur unitaire d'une ligne — échelle avatar + textes ("50" fixe, "50*" max, "*50" min, "*" remplit)
"PeriodHeaderHeight": 40, // (opt) Hauteur (px) de la ligne d'en-tête des périodes
"PeriodTextOrientation": "horizontal", // (opt) "horizontal" (défaut) ou "vertical" (texte pivoté)
"CornerCellText": "Équipe", // (opt) Texte de la cellule du coin haut-gauche
"HeaderColumnWidth": 220, // (opt) Largeur de la colonne des ressources (px)
"SubPeriodWidth": "200*" // (opt) Largeur d'une sous-colonne ("200" fixe, "200*" max, "*200" min, "*" remplit)
}
}
Remarks
Actuellement, seule la vue Planning d'équipe (renderer = 2, Team) est rendue.
Utilisez Calendar Helper V2 UTCAP.CreateTeamPlanRenderer pour construire la configuration.
CreateTask
Ajoute (ou met à jour) une tâche dans le calendrier.
procedure CreateTask(task: JsonObject)
Parameters
| Name | Type | Description |
|---|---|---|
| task | JsonObject |
Tâche à ajouter. |
Examples
La structure d'une tâche est la suivante :
{
"id": "t1", // Identifiant unique de la tâche
"assignedTo": "r1", // Identifiant de la ressource (ligne)
"startIndex": 2, // Index de colonne de début (0-based) = période × SubPeriodCount + sous-période
"endIndex": 3, // Index de colonne de fin (0-based, inclusif)
"text": "Réunion", // (opt) Texte principal
"secondaryText": "Salle 1", // (opt) Texte secondaire
"tertiaryText": "Bâtiment C", // (opt) Texte tertiaire
"quantity": 2, // (opt) Quantité, nombre OU texte (ex. "1/2") — pastille affichée UNIQUEMENT si ce champ est présent
"isEditable": true, // (opt) Autorise le déplacement / redimensionnement
"startingDateTime": "2026-01-01T08:00:00", // (opt) INFORMATIF — jamais utilisé pour le placement
"endingDateTime": "2026-01-01T10:00:00", // (opt) INFORMATIF — idem
"style": {
"background": "#21888c", // (opt) Couleur de fond
"foreground": "#ffffff", // (opt) Couleur du texte — imposée ; absente = calculée par contraste
"borderLeft": "#e74c3c" // (opt) Couleur de la bordure gauche
}
}
CreateTasks
Ajoute (ou met à jour) un ensemble de tâches dans le calendrier.
procedure CreateTasks(tasks: JsonArray)
Parameters
| Name | Type | Description |
|---|---|---|
| tasks | JsonArray |
Tableau de tâches à ajouter (voir |
DeleteTask
Supprime la tâche spécifiée.
procedure DeleteTask(taskId: Text)
Parameters
| Name | Type | Description |
|---|---|---|
| taskId | Text |
Identifiant de la tâche à supprimer. |
DeleteTasks
Supprime les tâches spécifiées.
procedure DeleteTasks(tasks: JsonArray)
Parameters
| Name | Type | Description |
|---|---|---|
| tasks | JsonArray |
Tableau des identifiants de tâches à supprimer. |
CreateResource
Ajoute (ou met à jour) une ressource dans le calendrier.
procedure CreateResource(resource: JsonObject)
Parameters
| Name | Type | Description |
|---|---|---|
| resource | JsonObject |
Ressource à ajouter. |
Examples
La structure d'une ressource est la suivante :
{
"id": "r1", // Identifiant unique de la ressource
"name": "Emilie Dupont", // Nom de la ressource
"description": "Technicienne", // (opt) Description
"imageColor": "#ffd574", // (opt) Couleur de l'anneau autour de l'image
"image": "iVBORw...", // (opt) Image encodée en Base64 (sans le préfixe data:)
"nameColor": "#d33", // (opt) Couleur du nom
"collapsed": false // (opt) Indique si la ressource est repliée
}
Remarks
Renvoyer une ressource déjà connue (même id) met à jour ses champs ; les champs optionnels absents restent inchangés.
CreateResources
Ajoute (ou met à jour) des ressources dans le calendrier.
procedure CreateResources(resources: JsonArray)
Parameters
| Name | Type | Description |
|---|---|---|
| resources | JsonArray |
Tableau de ressources à ajouter (voir |
DeleteResource
Supprime la ressource spécifiée.
procedure DeleteResource(id: Text)
Parameters
| Name | Type | Description |
|---|---|---|
| id | Text |
Identifiant de la ressource à supprimer du calendrier. |
DeleteResources
Supprime les ressources spécifiées du calendrier.
procedure DeleteResources(resources: JsonArray)
Parameters
| Name | Type | Description |
|---|---|---|
| resources | JsonArray |
Tableau des identifiants de ressources à supprimer. |
CreateMarker
Créé (ou met à jour) un marqueur sur le calendrier.
procedure CreateMarker(marker: JsonObject)
Parameters
| Name | Type | Description |
|---|---|---|
| marker | JsonObject |
Marqueur à ajouter. |
Examples
La structure d'un marqueur est la suivante :
{
"id": "mk1", // Identifiant unique du marqueur
"anchorType": "cell", // Type d'ancrage. Seul "cell" est rendu actuellement.
"assignedTo": "r1", // Ressource (ligne) sur laquelle poser le marqueur
"startIndex": 3, // Index de colonne de début (0-based)
"endIndex": 8, // Index de colonne de fin (0-based, inclusif)
"shape": "line", // "line" (bande hachurée) ou "triangle" (repère par cellule)
"title": "Indisponible", // (opt) Texte affiché au survol
"style": {
"color1": "#f1c40f", // (opt) Couleur principale
"color2": "#000" // (opt) Couleur secondaire (hachuré alterné pour "line")
}
}
Pour shape = line, le marqueur couvre une bande contiguë sur toute la plage de colonnes
(peut traverser plusieurs périodes). Pour shape = triangle, un triangle est affiché par cellule de la plage.
CreateMarkers
Créé (ou met à jour) les marqueurs sur le calendrier.
procedure CreateMarkers(markers: JsonArray)
Parameters
| Name | Type | Description |
|---|---|---|
| markers | JsonArray |
Tableau de marqueurs à ajouter (voir |
DeleteMarkers
Supprime tous les marqueurs du calendrier.
procedure DeleteMarkers()
CreateClosedPeriod
Créé (ou met à jour) une période fermée (férié, absence) : une bande hachurée et grisée, superposée à la grille sans empêcher le placement d'activités.
procedure CreateClosedPeriod(period: JsonObject)
Parameters
| Name | Type | Description |
|---|---|---|
| period | JsonObject |
Période fermée à ajouter. |
Examples
La structure d'une période fermée est la suivante :
{
"id": "cp1", // Identifiant unique de la période fermée
"startIndex": 4, // Index de colonne de début (0-based, inclusif)
"endIndex": 5, // Index de colonne de fin (0-based, inclusif)
"assignedTo": "r4", // (opt) Vide = toutes les ressources ; renseigné = une seule
"text": "Férié", // (opt) Tooltip
"style": {
"color1": "#e84393", // (opt) Couleur principale du hachuré
"color2": "#fff" // (opt) Couleur secondaire du hachuré
}
}
CreateClosedPeriods
Créé (ou met à jour) des périodes fermées sur le calendrier.
procedure CreateClosedPeriods(periods: JsonArray)
Parameters
| Name | Type | Description |
|---|---|---|
| periods | JsonArray |
Tableau de périodes fermées à ajouter (voir |
DeleteClosedPeriod
Supprime la période fermée spécifiée.
procedure DeleteClosedPeriod(id: Text)
Parameters
| Name | Type | Description |
|---|---|---|
| id | Text |
Identifiant de la période fermée à supprimer. |
DeleteClosedPeriods
Supprime les périodes fermées spécifiées.
procedure DeleteClosedPeriods(periods: JsonArray)
Parameters
| Name | Type | Description |
|---|---|---|
| periods | JsonArray |
Tableau des identifiants de périodes fermées à supprimer. |
SetTaskTooltip
Définit l'info-bulle pour une tâche.
procedure SetTaskTooltip(taskTooltip: JsonObject)
Parameters
| Name | Type | Description |
|---|---|---|
| taskTooltip | JsonObject |
Info-bulle à lier à une tâche. |
Examples
Structure d'une info-bulle :
{
"id": "t1", // Identifiant de la tâche à laquelle lier l'info-bulle
"content": "<b>Réunion</b>", // Contenu HTML libre de l'info-bulle
"cssClass": "" // (opt) Classe CSS additionnelle
}
Remarks
Vous ne pouvez pas appeler cette méthode pour une tâche qui n'existe pas.
Usage recommandé : en réponse à OnTaskTooltipRequested (chargement à la demande, au survol).
Le contenu HTML est injecté tel quel (source de confiance = BC) : n'y concaténez jamais d'entrée utilisateur non échappée.
SetTasksTooltip
Définit l'info-bulle à afficher pour un ensemble de tâches.
procedure SetTasksTooltip(taskTooltips: JsonArray)
Parameters
| Name | Type | Description |
|---|---|---|
| taskTooltips | JsonArray |
Tableau d'info-bulles (voir |
Remarks
Vous ne pouvez pas appeler cette méthode pour des tâches qui n'existent pas.
OpenTaskContextMenu
Ouvre un menu contextuel sur une tâche.
Cette méthode est généralement appelée en réaction à l'événement OnTaskContextMenuRequested.
procedure OpenTaskContextMenu(taskId: Text; items: JsonArray)
Parameters
| Name | Type | Description |
|---|---|---|
| taskId | Text |
Identifiant de la tâche pour laquelle ouvrir le menu. Il sera réutilisé par |
| items | JsonArray |
Liste des entrées du menu contextuel à afficher (voir |
OpenTasksContextMenu
Ouvre un menu contextuel pour PLUSIEURS tâches (multi-sélection).
Cette méthode est généralement appelée en réaction à l'événement OnTasksContextMenuRequested.
procedure OpenTasksContextMenu(taskIds: JsonArray; items: JsonArray)
Parameters
| Name | Type | Description |
|---|---|---|
| taskIds | JsonArray |
Identifiants des tâches pour lesquelles ouvrir le menu. Ils seront réutilisés par |
| items | JsonArray |
Liste des entrées du menu contextuel à afficher (voir |
OpenCalendarContextMenu
Déclenche l'ouverture du menu contextuel sur une cellule.
Cette méthode est généralement appelée en réaction à l'événement OnCalendarContextMenuRequested.
procedure OpenCalendarContextMenu(assignedTo: Text; index: Integer; items: JsonArray)
Parameters
| Name | Type | Description |
|---|---|---|
| assignedTo | Text |
Ressource (ligne) pour laquelle le menu doit être ouvert. |
| index | Integer |
Index de colonne (0-based) pour lequel le menu doit être ouvert. |
| items | JsonArray |
Liste des entrées du menu contextuel à afficher. |
Examples
Structure d'une entrée de menu contextuel :
{
"id": "delete", // Identifiant de l'entrée
"caption": "Supprimer", // Texte de l'entrée. Un tiret (-) affiche un séparateur.
"image": "Delete", // (opt) Nom de l'icône (Office UI Fabric Icons — https://uifabricicons.azurewebsites.net/)
"shortcut": "Ctrl+Suppr", // (opt) Raccourci affiché
"dangerous": true, // (opt) Style « action dangereuse » (rouge)
"subActions": [ /* entrées de sous-menu */ ] // (opt)
}
ExportToPDF
Demande au calendrier d'exporter le planning au format PDF (vectoriel), avec les options par défaut (une seule page dimensionnée au contenu ; ressources sans activité retirées). Le document généré est automatiquement téléchargé sur le poste de l'utilisateur.
procedure ExportToPDF()
Remarks
Le PDF est reconstruit VECTORIELLEMENT à partir du modèle (aucune rastérisation, aucune librairie) : rendu net à toute échelle, fichier léger. Export PDF uniquement.
ExportToPDF
Demande au calendrier d'exporter le planning au format PDF (vectoriel), avec options. Le document généré est automatiquement téléchargé sur le poste de l'utilisateur.
procedure ExportToPDF(fullView: Boolean; includeAll: Boolean; paged: Boolean)
Parameters
| Name | Type | Description |
|---|---|---|
| fullView | Boolean |
Réservé : |
| includeAll | Boolean |
|
| paged | Boolean |
|
Remarks
Le PDF est reconstruit VECTORIELLEMENT à partir du modèle (aucune rastérisation, aucune librairie) : rendu net à toute échelle, fichier léger. Export PDF uniquement.
Ping
Produit un événement Pong après le délai spécifié.
procedure Ping(Timeout: Integer)
Parameters
| Name | Type | Description |
|---|---|---|
| Timeout | Integer |
Délai en millisecondes jusqu'à la réception de l'événement |
HighlightPeriods
Met en surbrillance les périodes (colonnes principales) dont l'index (0-based) figure dans
indices. Chaque appel remplace la surbrillance précédente (non cumulatif).
Un tableau vide efface toute surbrillance de périodes.
procedure HighlightPeriods(indices: JsonArray)
Parameters
| Name | Type | Description |
|---|---|---|
| indices | JsonArray |
Tableau JSON d'entiers : index (0-based) des périodes à surligner. |
HighlightSubPeriods
Met en surbrillance des sous-périodes (créneaux) ciblées par un couple (période, sous-période) : une sous-période est toujours surlignée dans une période PRÉCISE (pas sur toutes). Chaque appel remplace la surbrillance précédente (non cumulatif). Un tableau vide l'efface.
procedure HighlightSubPeriods(pairs: JsonArray)
Parameters
| Name | Type | Description |
|---|---|---|
| pairs | JsonArray |
Tableau JSON de paires |
SetQuantityScale
Définit l'échelle (multiplicateur de transform: scale) de la pastille de quantité affichée
à droite d'une tâche : agrandit ou réduit le conteneur ENTIER de la pastille (cercle + texte).
1 correspond à 100 % (valeur par défaut). Aucune borne n'est appliquée (toute valeur
strictement positive est acceptée).
procedure SetQuantityScale(scale: Decimal)
Parameters
| Name | Type | Description |
|---|---|---|
| scale | Decimal |
Multiplicateur d'échelle (strictement positif ; |
Remarks
Réglage d'affichage DYNAMIQUE : il persiste entre les appels à SetRenderer (inutile de le
renvoyer à chaque changement de vue / de semaine). N'a d'effet que sur les tâches portant une
quantité (cf. Calendar Helper V2 UTCAP.SetTaskQuantity) ; sans pastille, aucun effet visible.