Jagamisviis:


Projekti ajakava API-de kasutamine ajastamise olemitega toimingute tegemiseks

Kehtib: Ressursipõhiste/mitteladustatud stsenaariumide projektitoimingud, Lite’i juurutamine – tehing proforma arveldamiseks.

Plaanimise olemid

Projekti ajakava API-d annavad võimaluse plaanimisolemitega loomis-, värskenda- ja kustutamistoiminguid teha. Neid olemeid hallatakse veebirakenduse Project ajastamise mootori kaudu. Toimingute loomine, värskendamine ja kustutamine plaanimisolemitega oli varasemates Dynamics 365 Project Operations väljaannetes piiratud.

Järgmises tabelid on toodud projekti ajakava olemite täielik loend.

Olemi nimi Olemi loogiline nimi
Project msdyn_project
Projekti ülesanne msdyn_projecttask
Projekti ülesande sõltuvus msdyn_projecttaskdependency
Ressursi määramine msdyn_resourceassignment
Projektisalv msdyn_projectbucket
Projektimeeskonna liige msdyn_projectteam
Projekti kontroll-loendid msdyn_projectchecklist
Projekti silt msdyn_projectlabel
Projekti ülesanne sildile msdyn_projecttasktolabel
Projekti sprint msdyn_projectsprint

OperationSet

OperationSet on tööühiku muster, mida saab kasutada juhul, kui kandes tuleb töödelda mitut ajakava mõjutavat taotlust.

Projekti ajakava API-d

Järgnev on praeguste projekti ajakava API-de loend.

API Kirjeldus
msdyn_CreateProjectV1 Seda API-t kasutatakse projekti loomiseks. Projekt ja vaikimisi projektisalv luuakse kohe. Projekti saab luua ka lisades projekti tabelisse rea, kasutades standardseid Dataverse API-sid. See protsess ei loo projektile vaike salv, kuid selle jõudlus võib olla parem.
msdyn_CreateTeamMemberV1 Seda API-t kasutatakse projektimeeskonna liikme loomiseks. Meeskonnaliikme kirje luuakse kohe. Meeskonnaliikme loomiseks saab standardsete Dataverse API-de abil lisada rea ka Project Team Memberi tabelisse.
msdyn_CreateOperationSetV1 Seda API-t saab kasutada mitme taotluse ajastamiseks, mis tuleb kande sees teha.
msdyn_PssCreateV1 Seda API-t kasutatakse olemi loomiseks. Olem võib olla mis tahes priekti ajastamise olemitest, mis toetab loomistoimingut.
msdyn_PssCreateV2 Seda API-t kasutatakse olemi loomiseks. See töötab nagu msdyn_PssCreateV1, kuid ühes toimingus saab luua mitu olemit.
msdyn_PssUpdateV1 Seda API-t kasutatakse olemi värskendamiseks. Olem võib olla mis tahes priekti ajastamise olemitest, mis toetab värskendamistoimingut.
msdyn_PssUpdateV2 Seda API-t kasutatakse olemite värskendamiseks. See töötab nagu msdyn_PssUpdateV1, kuid ühe toiminguga saab värskendada mitut olemit.
msdyn_PssDeleteV1 Seda API-t kasutatakse olemi kustutamiseks. Olem võib olla mis tahes priekti ajastamise olemitest, mis toetab kustutamistoimingut.
msdyn_PssDeleteV2 Seda API-d kasutatakse olemite kustutamiseks. See töötab nagu msdyn_PssDeleteV1, kuid ühe toiminguga saab kustutada mitu olemit.
msdyn_ExecuteOperationSetV1 Seda API-t kasutatakse antud toimingute komplektis kõikide toimingute täitmiseks.
msdyn_PssUpdateResourceAssignmentV1 Seda API-t kasutatakse ressursi määramise plaanitud töö kontuuri värskendamiseks.

Projecti ajakava API-de kasutamine operatsioonikomplektiga OperationSet

Kuna kirjed luuakse kohe nii CreateProjectV1 kui ka CreateTeamMemberV1 jaoks, ei saa neid API-sid otse operatsioonikomplektis kasutada. Siiski saate neid kasutada vajalike kirjete loomiseks, OperationSeti loomiseksja seejärel OperationSeti eelloodud kirjetekasutamiseks.

Toetatud tegevused

Olemi ajastamine Koosta Värskendus Kustutusklahv (Delete) Olulised kaalutlused
Projekti ülesanne Ja Ja Ja Välju Edenemine, Pingutus Lõpetatud ja PingutusSäilitamine saab redigeerida Project for the Webis, kuid neid ei saa redigeerida Project Operationsis.
Projektiülesande sõltuvus Ja No Ja Projektiülesande sõltuvuskirjeid ei värskendata. Selle asemel saab vana kirje kustutada ja luua uue kirje.
Ressursi määramine Ja Jah* Ja Järgmiste väljadega toimingud pole toetatud: BookableResourceID, Effort, EffortCompleted, EffortRemaining ja Planned Work.
Projektisalv Ja Ja Ja Vaike salv luuakse CreateProjectV1 API abil . Värskendusväljaandes 16 lisati projektisalvede loomise ja kustutamise tugi.
Projektimeeskonna liige Ja Ja Ja Loomise toimingu jaoks kasutage API-d CreateTeamMemberV1 .
Project Ja Ja No Järgmiste väljadega toiminguid ei toetata: StateCode,BulkGenerationStatus,GlobalRevisionToken,CalendarID,Effort,EffortCompleted,EffortRemaining,Progress,Finish,TaskEarliestStart ja Duration.
Projekti kontroll-loendid Ja Ja Ja
Projekti silt No Ja No Sildinimesid saab muuta. See funktsioon on saadaval ainult Projecti veebirakenduse jaoks. Sildid luuakse projekti esmakordsel avamisel.
Projekti ülesanne sildile Ja No Ja See funktsioon on saadaval ainult Projecti veebirakenduse jaoks.
Projekti sprint Ja Ja Ja Väljal Start peab olema väljast Lõpp varasem kuupäev. Sama projekti sprindid ei tohi üksteisega kattuda. See funktsioon on saadaval ainult Projecti veebirakenduse jaoks.
Projekti eesmärk Ja Ja Ja Järgmiste väljadega tehtavad toimingud pole toetatud: DescriptionPlainText, TaskDisplayOrder
Projekti ülesande ja eesmärgi seos Ja No Ja Järgmiste väljadega toimingud pole toetatud. TaskDisplayOrder

* Ressursimäärangu kirjeid ei värskendata. Selle asemel saab vana kirje kustutada ja luua uue kirje. Ressursside määramise kontuuride värskendamiseks on ette nähtud eraldi API.

ID atribuut on valikuline. Kui ID-atribuut on olemas, proovib süsteem seda kasutada ja viskab erandi, kui seda ei saa kasutada. Kui seda ei pakuta, loob süsteem selle.

Piirangud ja teadaolevad probleemid

Järgmine loend näitab piiranguid ja teadaolevaid probleeme.

  • Project Schedule’i API-sid saavad kasutada ainult Microsoft Projecti litsentsiga kasutajad. Neid ei saa kasutada järgnevad.

    • Rakenduse kasutajad
    • Süsteemi kasutajad
    • Integratsiooni kasutajad
    • Teised kasutajad, kes ei oma nõutavat litsentsi
  • Igal operatsioonikomplektil saab olla ainult maksimaalselt 200 toimingut.

  • Igal kasutajal saab olla ainult maksimaalselt 10 avatud operatsioonikomplekti.

  • Iga värskenduse ressursi määramise kontuuri toiming loetakse üheks toiminguks.

  • Iga värskendatud kontuuride loend võib sisaldada maksimaalselt 100 ajalõiku.

  • OperationSeti tõrkeoleku ja tõrkelogid pole praegu saadaval.

  • Ühe projekti kohta on maksimaalselt 400 sprinti.

  • Projektide ja ülesannete piirangud ja piirid.

Vigade käsitlemine

  • Operatsioonikomplektidest loodud tõrgete ülevaatamiseks minge jaotisse Sätete>ajakava Integratsioonitoimingute>komplektid.
  • Teenusest Project schedule Service loodud tõrgete ülevaatamiseks avage jaotis Sätete ajakava integreerimise>PSS-i tõrkelogid>.

Ressursside määramise kontuuride redigeerimine

Erinevalt kõigist teistest olemit värskendavatest projekti ajastamise API-dest vastutab ressursi määramise kontuuri API ainuüksi ühe välja msdyn_plannedwork värskenduste eest ühel olemil, msydn_resourceassignment.

Antud ajakavarežiim on:

  • fikseeritud ühikud.
  • Projekti kalender on esmaspäeval, teisipäeval, neljapäeval ja reedel kell 9.00–17.00 (Vaikse ookeani aeg). (Kolmapäeviti tööd ei ole.)
  • Ressursside kalender on esmaspäevast reedeni kella 9.00–13.00 (Vaikse ookeani aeg).

See ülesanne on üks nädal, neli tundi päevas, kuna ressursikalender on kella 9.00–13.00 (Vaikse ookeani aeg) või neli tundi päevas.

  Toiming Alguskuupäev Lõpukuupäev Kogus 13.06.2022 14.06.2022 15.06.2022 16.06.2022 17.06.2022
9-1 töötaja T1 13.06.2022 17.06.2022 20 4 4 4 4 4

Näiteks kui soovite, et töötaja töötaks sel nädalal iga päev ainult kolm tundi ja jätaks ühe tunni muude ülesannete jaoks.

UuendatudKontuuride näidis kasulik koormus

[{

"minutes":900.0,

"start":"2022-06-13T00:00:00-07:00",

"end":"2022-06-18T00:00:00-07:00"

}]

See on määramine pärast kontuuri ajakava värskendamise API käitamist.

  Toiming Alguskuupäev Lõpukuupäev Kogus 13.06.2022 14.06.2022 15.06.2022 16.06.2022 17.06.2022
9-1 töötaja T1 13.06.2022 17.06.2022 15 3 3 3 3 3

Stsenaariumi näidis

Selles stsenaariumis loote projekti, meeskonnaliikme, neli ülesannet ja kaks ressursimäärangut. Järgmisena värskendate ühte ülesannet, värskendate projekti, värskendate ressursimäärangu kontuuri, kustutate ühe ülesande, kustutate ühe ressursiülesande ja loote ülesande sõltuvuse.

Entity project = CreateProject();
project.Id = CallCreateProjectAction(project);
var projectReference = project.ToEntityReference();

var teamMember = new Entity("msdyn_projectteam", Guid.NewGuid());
teamMember["msdyn_name"] = $"TM {DateTime.Now.ToShortTimeString()}";
teamMember["msdyn_project"] = projectReference;
var createTeamMemberResponse = CallCreateTeamMemberAction(teamMember);

var description = $"My demo {DateTime.Now.ToShortTimeString()}";
var operationSetId = CallCreateOperationSetAction(project.Id, description);

var task1 = GetTask("1WW", projectReference);
var task2 = GetTask("2XX", projectReference, task1.ToEntityReference());
var task3 = GetTask("3YY", projectReference);
var task4 = GetTask("4ZZ", projectReference);

var assignment1 = GetResourceAssignment("R1", teamMember, task2, project);
var assignment2 = GetResourceAssignment("R2", teamMember, task3, project);

var task1Response = CallPssCreateAction(task1, operationSetId);
var task2Response = CallPssCreateAction(task2, operationSetId);
var task3Response = CallPssCreateAction(task3, operationSetId);
var task4Response = CallPssCreateAction(task4, operationSetId);

var assignment1Response = CallPssCreateAction(assignment1, operationSetId);
var assignment2Response = CallPssCreateAction(assignment2, operationSetId);

task2["msdyn_subject"] = "Updated Task";
var task2UpdateResponse = CallPssUpdateAction(task2, operationSetId);

project["msdyn_subject"] = $"Proj update {DateTime.Now.ToShortTimeString()}";
var projectUpdateResponse = CallPssUpdateAction(project, operationSetId);

List<UpdatedContour> updatedContours = new List<UpdatedContour>(); 
UpdatedContour updatedContour = new UpdatedContour(); 
updatedContour.Start = DateTime.UtcNow.Date; 
updatedContour.End = DateTime.UtcNow.Date.AddDays(1); 
updatedContour.Minutes = 120; 
updatedContours.Add(updatedContour); 

String serializedUpdate = JsonConvert.SerializeObject(updatedContours); 
var updateContoursResponse = CallPssUpdateContourAction(assignment1.Id, serializedUpdate, operationSetId); 

var task4DeleteResponse = CallPssDeleteAction(task4.Id.ToString(), task4.LogicalName, operationSetId);

var assignment2DeleteResponse = CallPssDeleteAction(assignment2.Id.ToString(), assignment2.LogicalName, operationSetId);

var dependency1 = GetTaskDependency(project, task2, task3);
var dependency1Response = CallPssCreateAction(dependency1, operationSetId);

CallExecuteOperationSetAction(operationSetId);
Console.WriteLine("Done....");

Täiendavad proovid

#region Call actions --- Sample code ----

/// <summary>
/// Calls the action to create an operationSet
/// </summary>
/// <param name="projectId">project id for the operations to be included in this operationSet</param>
/// <param name="description">description of this operationSet</param>
/// <returns>operationSet id</returns>
private string CallCreateOperationSetAction(Guid projectId, string description)
{
    OrganizationRequest operationSetRequest = new OrganizationRequest("msdyn_CreateOperationSetV1");
    operationSetRequest["ProjectId"] = projectId.ToString();
    operationSetRequest["Description"] = description;
    OrganizationResponse response = organizationService.Execute(operationSetRequest);
    return response["OperationSetId"].ToString();
}

/// <summary>
/// Calls the action to create an entity
/// </summary>
/// <param name="entity">Scheduling entity</param>
/// <param name="operationSetId">operationSet id</param>
/// <returns>OperationSetResponse</returns>

private OperationSetResponse CallPssCreateAction(Entity entity, string operationSetId)
{
    OrganizationRequest operationSetRequest = new OrganizationRequest("msdyn_PssCreateV1");
    operationSetRequest["Entity"] = entity;
    operationSetRequest["OperationSetId"] = operationSetId;
    return GetOperationSetResponseFromOrgResponse(organizationService.Execute(operationSetRequest));
}

/// <summary>
/// Calls the action to update an entity
/// </summary>
/// <param name="entity">Scheduling entity</param>
/// <param name="operationSetId">operationSet Id</param>
/// <returns>OperationSetResponse</returns>
private OperationSetResponse CallPssUpdateAction(Entity entity, string operationSetId)
{
    OrganizationRequest operationSetRequest = new OrganizationRequest("msdyn_PssUpdateV1");
    operationSetRequest["Entity"] = entity;
    operationSetRequest["OperationSetId"] = operationSetId;
    return GetOperationSetResponseFromOrgResponse(organizationService.Execute(operationSetRequest));
}

/// <summary>
/// Calls the action to update an entity
/// </summary>
/// <param name="recordId">Id of the record to be deleted</param>
/// <param name="entityLogicalName">Entity logical name of the record</param>
/// <param name="operationSetId">OperationSet Id</param>
/// <returns>OperationSetResponse</returns>
private OperationSetResponse CallPssDeleteAction(string recordId, string entityLogicalName, string operationSetId)
{
    OrganizationRequest operationSetRequest = new OrganizationRequest("msdyn_PssDeleteV1");
    operationSetRequest["RecordId"] = recordId;
    operationSetRequest["EntityLogicalName"] = entityLogicalName;
    operationSetRequest["OperationSetId"] = operationSetId;
    return GetOperationSetResponseFromOrgResponse(organizationService.Execute(operationSetRequest));
}

/// <summary> 
/// Calls the action to update a Resource Assignment contour
/// </summary> 
/// <param name="resourceAssignmentId">Id of the resource assignment to be updated</param> 
/// <param name="serializedUpdates">JSON formatted contour updates</param>
/// <param name="operationSetId">operationSet id</param> 
/// <returns>OperationSetResponse</returns> 
private OperationSetResponse CallPssUpdateContourAction(string resourceAssignmentId, string serializedUpdates string operationSetId) 
{
    OrganizationRequest operationSetRequest = new OrganizationRequest("msdyn_PssUpdateResourceAssignmentContourV1"); 
    operationSetRequest["ResourceAssignmentId"] = resourceAssignmentId; 
    operationSetRequest["UpdatedContours"] = serializedUpdates; 
    operationSetRequest["OperationSetId"] = operationSetId; 
    return GetOperationSetResponseFromOrgResponse(OrganizationService.Execute(operationSetRequest)); 
} 

/// <summary>
/// Calls the action to execute requests in an operationSet
/// </summary>
/// <param name="operationSetId">operationSet id</param>
/// <returns>OperationSetResponse</returns>
private OperationSetResponse CallExecuteOperationSetAction(string operationSetId)
{
    OrganizationRequest operationSetRequest = new OrganizationRequest("msdyn_ExecuteOperationSetV1");
    operationSetRequest["OperationSetId"] = operationSetId;
    return GetOperationSetResponseFromOrgResponse(organizationService.Execute(operationSetRequest));
}

/// <summary>
/// This can be used to abandon an operationSet that is no longer needed
/// </summary>
/// <param name="operationSetId">operationSet id</param>
/// <returns>OperationSetResponse</returns>
protected OperationSetResponse CallAbandonOperationSetAction(Guid operationSetId)
{
    OrganizationRequest operationSetRequest = new OrganizationRequest("msdyn_AbandonOperationSetV1");
    operationSetRequest["OperationSetId"] = operationSetId.ToString();
    return GetOperationSetResponseFromOrgResponse(organizationService.Execute(operationSetRequest));
}


/// <summary>
/// Calls the action to create a new project
/// </summary>
/// <param name="project">Project</param>
/// <returns>project Id</returns>
private Guid CallCreateProjectAction(Entity project)
{
    OrganizationRequest createProjectRequest = new OrganizationRequest("msdyn_CreateProjectV1");
    createProjectRequest["Project"] = project;
    OrganizationResponse response = organizationService.Execute(createProjectRequest);
    var projectId = Guid.Parse((string)response["ProjectId"]);
    return projectId;
}

/// <summary>
/// Calls the action to create a new project team member
/// </summary>
/// <param name="teamMember">Project team member</param>
/// <returns>project team member Id</returns>
private string CallCreateTeamMemberAction(Entity teamMember)
{
    OrganizationRequest request = new OrganizationRequest("msdyn_CreateTeamMemberV1");
    request["TeamMember"] = teamMember;
    OrganizationResponse response = organizationService.Execute(request);
    return (string)response["TeamMemberId"];
}

private OperationSetResponse GetOperationSetResponseFromOrgResponse(OrganizationResponse orgResponse)
{
    return JsonConvert.DeserializeObject<OperationSetResponse>((string)orgResponse.Results["OperationSetResponse"]);
}

private EntityCollection GetDefaultBucket(EntityReference projectReference)
{
    var columnsToFetch = new ColumnSet("msdyn_project", "msdyn_name");
    var getDefaultBucket = new QueryExpression("msdyn_projectbucket")
    {
        ColumnSet = columnsToFetch,
        Criteria =
        {
            Conditions =
            {
                new ConditionExpression("msdyn_project", ConditionOperator.Equal, projectReference.Id),
                new ConditionExpression("msdyn_name", ConditionOperator.Equal, "Bucket 1")
            }
        }
    };

    return organizationService.RetrieveMultiple(getDefaultBucket);
}

private Entity GetBucket(EntityReference projectReference)
{
    var bucketCollection = GetDefaultBucket(projectReference);
    if (bucketCollection.Entities.Count > 0)
    {
        return bucketCollection[0].ToEntity<Entity>();
    }

    throw new Exception($"Please open project with id {projectReference.Id} in the Dynamics UI and navigate to the Tasks tab");
}

private Entity CreateProject()
{
    var project = new Entity("msdyn_project", Guid.NewGuid());
    project["msdyn_subject"] = $"Proj {DateTime.Now.ToShortTimeString()}";

    return project;
}



private Entity GetTask(string name, EntityReference projectReference, EntityReference parentReference = null)
{
    var task = new Entity("msdyn_projecttask", Guid.NewGuid());
    task["msdyn_project"] = projectReference;
    task["msdyn_subject"] = name;
    task["msdyn_effort"] = 4d;
    task["msdyn_scheduledstart"] = DateTime.Today;
    task["msdyn_scheduledend"] = DateTime.Today.AddDays(5);
    task["msdyn_start"] = DateTime.Now.AddDays(1);
    task["msdyn_projectbucket"] = GetBucket(projectReference).ToEntityReference();
    task["msdyn_LinkStatus"] = new OptionSetValue(192350000);

    //Custom field handling
    /*
    task["new_custom1"] = "Just my test";
    task["new_age"] = 98;
    task["new_amount"] = 591.34m;
    task["new_isready"] = new OptionSetValue(100000000);
    */

    if (parentReference == null)
    {
        task["msdyn_outlinelevel"] = 1;
    }
    else
    {
        task["msdyn_parenttask"] = parentReference;
    }

    return task;
}

private Entity GetResourceAssignment(string name, Entity teamMember, Entity task, Entity project)
{
    var assignment = new Entity("msdyn_resourceassignment", Guid.NewGuid());
    assignment["msdyn_projectteamid"] = teamMember.ToEntityReference();
    assignment["msdyn_taskid"] = task.ToEntityReference();
    assignment["msdyn_projectid"] = project.ToEntityReference();
    assignment["msdyn_name"] = name;
   
    return assignment;
}

protected Entity GetTaskDependency(Entity project, Entity predecessor, Entity successor)
{
    var taskDependency = new Entity("msdyn_projecttaskdependency", Guid.NewGuid());
    taskDependency["msdyn_project"] = project.ToEntityReference();
    taskDependency["msdyn_predecessortask"] = predecessor.ToEntityReference();
    taskDependency["msdyn_successortask"] = successor.ToEntityReference();
    taskDependency["msdyn_linktype"] = new OptionSetValue(192350000);

    return taskDependency;
}

#endregion


#region OperationSetResponse DataContract --- Sample code ----

[DataContract]
public class OperationSetResponse
{
[DataMember(Name = "operationSetId")]
public Guid OperationSetId { get; set; }

[DataMember(Name = "operationSetDetailId")]
public Guid OperationSetDetailId { get; set; }

[DataMember(Name = "operationType")]
public string OperationType { get; set; }

[DataMember(Name = "recordId")]
public string RecordId { get; set; }

[DataMember(Name = "correlationId")]
public string CorrelationId { get; set; }
}

#endregion

#region UpdatedContour DataContract --- Sample code ---- 

[DataContract] 
public class UpdatedContour 
{ 
[DataMember(Name = "start")] 
public DateTime Start { get; set; } 

[DataMember(Name = "end")] 
public DateTime End { get; set; } 

[DataMember(Name = "minutes")] 
public decimal Minutes { get; set; } 
} 

#endregion