No hace mucho vi un video de Erik Hougaard donde se enfrentaba al desafÃo de añadir un diagrama de gantt en Business central para mostrar los proyectos, aquà os dejo el link por si os interesa.
Recomiendo mucho ver sus videos. Yo he aprendido muchÃsimo gracias a él sobre todo en cuanto a addins se refiere.Él utiliza otro componente para mostrarlo.
Por la licencia que utiliza este componente voy a hablar de Frappe gantt
Aquà podemos ver una demo funcional y la verdad es que está bastante bien ya que permite arrastrar y modificar elementos con el ratón.
A simple vista es bastante funcional y nos permite añadir popups con información y eventos con lo que podremos crear una comunicación completa entre el control y Business central.
En nuestro proyecto crearemos una nueva carpeta a la que llamaremos .... Addin 😂 (las viejas costumbres no se pierden).
En el repositorio localizamos la carpeta gantt\dist y dentro de ella necesitaremos los ficheros frappe-gantt.min.css y frappe-gantt.min.js, los descargamos en nuestra carpeta Addin.
Dentro de esta carpeta crearemos un nuevo fichero llamado gantt.al en el que definimos nuestro control, lo llamaremos gantt.al
controladdin "gantt"
{
VerticalStretch = true;
VerticalShrink = true;
HorizontalStretch = true;
HorizontalShrink = true;
RequestedWidth = 500;
RequestedHeight = 600;
Scripts = './Addin/frappe-gantt.min.js';
StartupScript = './Addin/startup.js';
StyleSheets = './Addin/frappe-gantt.min.css';
event Ready();
procedure initgraph(tasks: JsonArray);
}
Ahora debemos crear el script de inicio, al que llamaremos startup.js dentro de la misma carpeta, pero antes nos vamos a fijar en cómo se construye una llamada a un ejemplo, para ellos vamos a la carpeta raÃz de repositorio github y localizamos el fichero index.html
Observamos que la llamada es muy simple (código encerrado entre div), después genera un JSON Array para los datos y por último ejecuta el script para mostrar el diagrama.
En la definición del control he creado una función mediante la cual pasamos los datos al control con un JSONArray como parámetro, con lo que vamos a construir nuestro startup.js
Microsoft.Dynamics.NAV.InvokeExtensibilityMethod('Ready');
window.initgraph = function(tasks){
try{
element = document.getElementById('controlAddIn');
element.innerHTML = `<div class="container">
<h2 class="heading">Interactive Gantt Chart entirely made in SVG!</h2>
<div class="gantt-target"></div>
</div>`;
var gantt_chart = new Gantt(".gantt-target", tasks, {
on_click: task => {
console.log(task);
},
on_date_change: (task, start, end) => {
console.log(task, start, end);
},
on_progress_change: (task, progress) => {
console.log(task, progress);
},
on_view_change: (mode) => {
console.log(mode);
},
view_mode: 'Month',
language: 'es'
});
} catch (err) {
console.log(err);
}
}
una lÃnea con un texto o cabecera y el div con el control que luego se encargará la librerÃa de sustituir gantt-target.
Por el momento lo dejamos asÃ, simplemente queremos comprobar que somos capaces de hacerlo funcionar y de que tenemos el control.
Vamos a crear una página donde mostrar el control, debemos añadir el control y al iniciarla generar el JSONArray
page 50900 Gantt
{
ApplicationArea = All;
Caption = 'Gantt';
PageType = Card;
UsageCategory = Tasks;
layout
{
area(content)
{
group(General)
{
Caption = 'General';
usercontrol(gantt; gantt)
{
trigger Ready()
begin
CurrPage.gantt.initgraph(JArray);
end;
}
}
}
}
trigger OnOpenPage()
var
Job: Record Job;
JobTask: Record "Job Task";
JObject: JsonObject;
begin
Job.SetLoadFields("Starting Date", "Ending Date", Description, "No.");
if Job.FindSet() then
repeat
Clear(JObject);
JObject.Add('start', FORMAT(Job."Starting Date", 0, 9));
JObject.Add('end', FORMAT(Job."Ending Date", 0, 9));
JObject.Add('name', Job.Description);
JObject.Add('id', Job."No.");
JObject.Add('progress', Job.PercentCompleted());
JArray.Add(JObject);
JobTask.Reset();
JobTask.SetRange("Job No.", Job."No.");
JobTask.SetRange("Job Task Type", JobTask."Job Task Type"::Posting);
JobTask.SetLoadFields("Start Date", "End Date", Description, "Job Task No.");
if JobTask.FindSet() then
repeat
Clear(JObject);
JobTask.CalcFields("Start Date", "End Date");
JObject.Add('start', FORMAT(JobTask."Start Date", 0, 9));
JObject.Add('end', FORMAT(JobTask."End Date", 0, 9));
JObject.Add('name', JobTask.Description);
JObject.Add('id', JobTask."Job Task No.");
JObject.Add('progress', Job.PercentCompleted());
JObject.Add('dependencies', Job."No.");
JArray.Add(JObject);
until JobTask.Next() = 0;
until job.Next() = 0;
end;
var
JArray: JsonArray;
}
Vemos que el resultado es bastante aceptable (seguramente el diseño sea mejorable ... tocarÃa modificar el css)
Volvemos de nuevo a la página del control donde mostraba los ejemplos y nos interesamos en el apartado Add event listeners.
El resultado es este:
Microsoft.Dynamics.NAV.InvokeExtensibilityMethod('Ready');
window.initgraph = function(tasks){
try{
element = document.getElementById('controlAddIn');
element.innerHTML = `<div class="container">
<div class="gantt-target"></div>
</div>`;
var gantt_chart = new Gantt(".gantt-target", tasks, {
on_click: task => {
console.log(task);
},
on_date_change: (task, start, end) => {
Microsoft.Dynamics.NAV.InvokeExtensibilityMethod('datechanged',[task.name, start, end]);
console.log(task, start, end);
},
on_progress_change: (task, progress) => {
Microsoft.Dynamics.NAV.InvokeExtensibilityMethod('progresschanged',[task.name, progress]);
console.log(task, progress);
},
on_view_change: (mode) => {
console.log(mode);
},
view_mode: 'Month',
language: 'es'
});
} catch (err) {
console.log(err);
}
}
(He aprovechado para quitar la lÃnea de cabecera donde ponÃa Interactive Gantt Chart ... ) 😉
Ahora debemos capacitar a Business central para recibir esos valores, en nuestro fichero gantt.al añadimos los eventos datechanged y progresschanged
controladdin "gantt"
{
VerticalStretch = true;
VerticalShrink = true;
HorizontalStretch = true;
HorizontalShrink = true;
RequestedWidth = 500;
RequestedHeight = 600;
Scripts = './Addin/frappe-gantt.min.js';
StartupScript = './Addin/startup.js';
StyleSheets = './Addin/frappe-gantt.min.css';
event Ready();
procedure initgraph(tasks: JsonArray);
event datechanged(task: Text; startdate: Text; enddate: Text);
event progresschanged(task: Text; progress: Decimal);
}
y por último, en la página donde mostramos el control, añadimos los eventos para poder actuar con ellos
page 50900 Gantt
{
ApplicationArea = All;
Caption = 'Gantt';
PageType = Card;
UsageCategory = Tasks;
layout
{
area(content)
{
group(General)
{
ShowCaption = false;
usercontrol(gantt; gantt)
{
trigger Ready()
begin
CurrPage.gantt.initgraph(JArray);
end;
trigger datechanged(task: Text; startdate: Text; enddate: Text)
var
Msg001: Label 'El evento %1 ha modificado sus fechas a inicio: %2 fin:%3';
begin
Message(StrSubstNo(Msg001, task, startdate, enddate));
end;
trigger progresschanged(task: Text; progress: Decimal)
var
Msg001: Label 'El evento %1 ha modificado sus progreso a %2 %';
begin
Message(StrSubstNo(Msg001, task, progress));
end;
}
}
}
}
trigger OnOpenPage()
var
Job: Record Job;
JobTask: Record "Job Task";
JObject: JsonObject;
begin
Job.SetLoadFields("Starting Date", "Ending Date", Description, "No.");
if Job.FindSet() then
repeat
Clear(JObject);
JObject.Add('start', FORMAT(Job."Starting Date", 0, 9));
JObject.Add('end', FORMAT(Job."Ending Date", 0, 9));
JObject.Add('name', Job.Description);
JObject.Add('id', Job."No.");
JObject.Add('progress', Job.PercentCompleted());
JArray.Add(JObject);
JobTask.Reset();
JobTask.SetRange("Job No.", Job."No.");
JobTask.SetRange("Job Task Type", JobTask."Job Task Type"::Posting);
JobTask.SetLoadFields("Start Date", "End Date", Description, "Job Task No.");
if JobTask.FindSet() then
repeat
Clear(JObject);
JobTask.CalcFields("Start Date", "End Date");
JObject.Add('start', FORMAT(JobTask."Start Date", 0, 9));
JObject.Add('end', FORMAT(JobTask."End Date", 0, 9));
JObject.Add('name', JobTask.Description);
JObject.Add('id', JobTask."Job Task No.");
JObject.Add('progress', Job.PercentCompleted());
JObject.Add('dependencies', Job."No.");
JArray.Add(JObject);
until JobTask.Next() = 0;
until job.Next() = 0;
end;
var
JArray: JsonArray;
}
El resultado es el siguiente (sólo muestro un mensaje, aquà deberÃamos modificar datos siempre que sea posible y si no lo fuera, dibujamos de nuevo el control llamando a la función initgraph
Resultado de modificar una tarea arrastrando un lateral incrementando la duración
Resultado de modificar una tarea incrementando el progreso
Una modificación interesante serÃa añadir el control en la página de la ficha de los proyectos dando como resultado lo siguiente
la extensión de la página es la siguiente:pageextension 50900 JobCardExt extends "Job Card"
{
layout
{
addafter(JobTaskLines)
{
group(Gantt)
{
usercontrol(mygantt; gantt)
{
trigger Ready()
begin
CurrPage.mygantt.initgraph(JArray);
end;
}
}
}
}
trigger OnAfterGetRecord()
var
JobTask: Record "Job Task";
JObject: JsonObject;
begin
Clear(JObject);
JObject.Add('start', FORMAT(Rec."Starting Date", 0, 9));
JObject.Add('end', FORMAT(Rec."Ending Date", 0, 9));
JObject.Add('name', Rec.Description);
JObject.Add('id', Rec."No.");
JObject.Add('progress', Rec.PercentCompleted());
JArray.Add(JObject);
JobTask.Reset();
JobTask.SetRange("Job No.", Rec."No.");
JobTask.SetRange("Job Task Type", JobTask."Job Task Type"::Posting);
JobTask.SetLoadFields("Start Date", "End Date", Description, "Job Task No.");
if JobTask.FindSet() then
repeat
Clear(JObject);
JobTask.CalcFields("Start Date", "End Date");
JObject.Add('start', FORMAT(JobTask."Start Date", 0, 9));
JObject.Add('end', FORMAT(JobTask."End Date", 0, 9));
JObject.Add('name', JobTask.Description);
JObject.Add('id', JobTask."Job Task No.");
JObject.Add('progress', Rec.PercentCompleted());
JObject.Add('dependencies', Rec."No.");
JArray.Add(JObject);
until JobTask.Next() = 0;
end;
var
JArray: JsonArray;
}
Espero que os sirva de ayuda. Lo interesante es el concepto de cómo montar un control desde cero.
Como siempre el código disponible en github.
Publicar un comentario