Na vykonávanie operácií s entitami plánovania použite rozhrania API pre plánovanie projektu

Vzťahuje sa na: Projektové operácie pre scenáre založené na zdrojoch/nezásobách, Lite nasadenie – dohoda s proforma fakturáciou.

Plánovacie entity

Rozhrania API plánovania projektu poskytujú možnosť vykonávať operácie vytvárania, aktualizácie a odstraňovania pomocou entít plánovania. Tieto entity sú spravované prostredníctvom modulu Plánovanie v Projekte pre web. Operácie vytvárania, aktualizácie a odstraňovania s entitami plánovania boli v starších Dynamics 365 Project Operations vydaniach obmedzené.

Nasledujúca tabuľka poskytuje úplný zoznam entít plánovania projektu.

Názov entity Logický názov entity
Project msdyn_project
Projektová úloha msdyn_projecttask
Závislosť projektovej úlohy msdyn_projecttaskdependency
Priradenie zdroja msdyn_resourceassignment
Projektový kontajner msdyn_projectbucket
Člen projektového tímu msdyn_projectteam
Kontrolné zoznamy projektu msdyn_projectchecklist
Označenie projektu msdyn_projectlabel
Projektová úloha s označením msdyn_projecttasktolabel
Šprint projektu msdyn_projectsprint

OperationSet

OperationSet je vzor jednotky práce, ktorý je možné použiť, keď sa v rámci transakcie musí spracovať niekoľko požiadaviek ovplyvňujúcich plán.

Rozhrania API plánovania projektu

Nasleduje zoznam aktuálnych rozhraní API plánovania projektu.

Rozhranie API Description
msdyn_CreateProjectV1 Toto API sa používa na vytvorenie projektu. Projekt a predvolený kontajner projektu sa vytvoria okamžite. Vytvorenie projektu je možné vykonať aj pridaním riadka do tabuľky projektov pomocou štandardných Dataverse API. Tento proces nevytvorí predvolený segment pre projekt, ale môže mať lepší výkon.
msdyn_CreateTeamMemberV1 Toto API sa používa na vytvorenie člena tímu. Záznam o členovi tímu sa vytvorí okamžite. Vytvorenie člena tímu je možné vykonať aj pridaním riadka do tabuľky členov projektového tímu pomocou štandardných Dataverse API.
msdyn_CreateOperationSetV1 Toto API sa používa na naplánovanie niekoľkých požiadaviek, ktoré sa musia vykonať v rámci transakcie.
msdyn_PssCreateV1 Toto API sa používa na vytvorenie entity. Entitou môže byť ktorákoľvek z entít plánovania projektu, ktoré podporujú operáciu vytvorenia.
msdyn_PssCreateV2 Toto API sa používa na vytvorenie entity. Funguje to ako msdyn_PssCreateV1, ale v jednej akcii možno vytvoriť viacero entít.
msdyn_PssUpdateV1 Toto API sa používa na aktualizáciu entity. Entitou môže byť ktorákoľvek z entít plánovania projektu, ktoré podporujú operáciu aktualizácie.
msdyn_PssUpdateV2 Toto API sa používa na aktualizované entity. Funguje to ako msdyn_PssUpdateV1, ale v jednej akcii je možné aktualizovať viacero entít.
msdyn_PssDeleteV1 Toto API sa používa na odstránenie entity. Entitou môže byť ktorákoľvek z entít plánovania projektu, ktoré podporujú operáciu vymazania.
msdyn_PssDeleteV2 Toto API sa používa na mazanie entít. Funguje to ako msdyn_PssDeleteV1, ale naraz je možné odstrániť viacero entít.
msdyn_ExecuteOperationSetV1 Toto API sa používa na vykonávanie všetkých operácií v rámci danej množiny operácií.
msdyn_PssUpdateResourceAssignmentV1 Toto API sa používa na aktualizáciu kontúry plánovanej práce priradenia zdroja.

Používanie rozhraní API plánovania projektu s OperationSet

Keďže záznamy sa vytvárajú okamžite pre CreateProjectV1 a CreateTeamMemberV1, tieto rozhrania API nemožno použiť priamo v Operačná sada. Môžete ich však použiť na vytvorenie požadovaných záznamov, vytvorenie OperationSet a potom použiť vopred vytvorené záznamy v OperationSet.

Podporované operácie

Entita na plánovanie Vytvoriť Update Delete Dôležité aspekty
Projektová úloha Áno Áno Áno Polia Pokrok, Dokončené úsilie a Zostávajúce úsilie možno upraviť v Projecte pre web, ale nie je možné ich upraviť v Project Operations.
Závislosť projektovej úlohy Áno No Áno Záznamy o závislosti od projektových úloh sa neaktualizujú. Namiesto toho je možné odstrániť starý záznam a vytvoriť nový záznam.
Priradenie zdroja Áno Áno* Áno Operácie s nasledujúcimi poľami nie sú podporované: BookableResourceID, Effort, EffortCompleted , Zostávajúce úsilie a Plánovaná práca.
Projektový kontajner Áno Áno Áno Predvolený segment sa vytvorí pomocou CreateProjectV1 API. V aktualizácii vydania 16 bola pridaná podpora na vytváranie a odstraňovanie kontajnerov projektov.
Člen projektového tímu Áno Áno Áno Na operáciu vytvorenia použite CreateTeamMemberV1 API.
Project Áno Áno Operácie s nasledujúcimi poľami nie sú podporované: StateCode, BulkGenerationStatus, GlobalRevisionToken , ID kalendára, Úsilie, Úsilie dokončené, Zostávajúce úsilie, Pokrok, Dokončiť, Najskorší začiatok úlohy a Trvanie.
Kontrolné zoznamy projektu Áno Áno Áno
Označenie projektu No Áno No Názvy označení je možné zmeniť. Táto funkcia je dostupná len pre Project for the Web. Štítky sa vytvárajú pri prvom otvorení projektu.
Projektová úloha s označením Áno No Áno Táto funkcia je dostupná len pre Project for the Web.
Šprint projektu Áno Áno Áno Pole Začiatok musí mať skorší dátum ako pole Dokončiť . Šprinty toho istého projektu sa nemôžu navzájom prekrývať. Táto funkcia je dostupná len pre Project for the Web.
Cieľ projektu Áno Áno Áno Operácie s nasledujúcimi poľami nie sú podporované: DescriptionPlainText, TaskDisplayOrder
Úloha pre cieľ projektu Áno No Áno Operácie s nasledujúcimi poľami nie sú podporované: TaskDisplayOrder

* Záznamy priradenia zdrojov sa neaktualizujú. Namiesto toho je možné odstrániť starý záznam a vytvoriť nový záznam. Na aktualizáciu kontúr priradenia zdrojov bolo poskytnuté samostatné rozhranie API.

Toto ID vlastnosti je voliteľné. Ak je poskytnutá, systém sa ju pokúsi použiť a ak ju nemožno použiť, vyvolá výnimku. Ak nie je poskytnutý, systém ho vygeneruje.

Obmedzenia a známe problémy

Nasleduje zoznam obmedzení a známych problémov:

  • Rozhrania Project Schedule API môžu používať iba Používatelia s licenciou Microsoft Project. Nemôžu ich používať:

    • Používatelia aplikácie
    • Systémoví používatelia
    • Používatelia integrácie
    • Ostatní používatelia, ktorí nemajú požadovanú licenciu
  • Každý OperationSet môže mať maximálne 200 operácií.

  • Každý používateľ môže mať otvorených maximálne 10 OperationSet.

  • Project Operations v súčasnosti podporuje v projekte celkovo maximálne 1000 úloh.

  • Každá operácia aktualizácie kontúr priradenia zdrojov sa počíta ako jedna operácia.

  • Každý zoznam aktualizovaných kontúr môže obsahovať maximálne 100 časových rezov.

  • Stav zlyhania OperationSet a protokoly porúch nie sú momentálne k dispozícii.

  • Na jeden projekt je maximálne 400 šprintov.

  • Limity a hranice projektov a úloh.

  • Označenia sú momentálne dostupné iba pre Project for the Web.

  • Štítky sa vytvárajú pri prvom otvorení projektu.

  • Každý projekt má maximálne 10 cieľov.

  • Každá úloha sa môže objaviť v časti Project Task to Goal raz.

Spracovanie chýb

  • Ak chcete skontrolovať chyby generované zo sád operácií, prejdite na Nastavenia>Integrácia plánu>Súpravy operácií.
  • Ak chcete skontrolovať chyby generované službou plánovania projektu, prejdite na Nastavenia>Integrácia plánu>Protokoly chýb PSS.

Úprava obrysov priradenia zdrojov

Na rozdiel od všetkých ostatných API plánovania projektov, ktoré aktualizujú entitu, API kontúry priradenia zdrojov je výhradne zodpovedné za aktualizácie jedného poľa – msdyn_plannedwork – v jednej entite – msydn_resourceassignment.

Daný režim plánovania je:

  • pevné jednotky.
  • Kalendár projektu je od 9:00 do 17:00 (tichomorského času) v pondelok, utorok, štvrtok a piatok. (V stredu sa nepracuje.)
  • Kalendár zdrojov je od pondelka do piatku od 9:00 do 13:00 (tichomorského času).

Táto úloha je na jeden týždeň, štyri hodiny denne. Je to preto, že kalendár zdrojov je od 9:00 do 13:00 (tichomorského času) alebo štyri hodiny denne.

  Úloha Počiatočný dátum Konečný dátum Množstvo 13. 6. 2022 14. 6. 2022 15. 6. 2022 16. 6. 2022 17. 6. 2022
9-1 pracovník T1 13. 6. 2022 17. 6. 2022 20 4 4 4 4 4

Napríklad, ak chcete, aby pracovník tento týždeň pracoval iba tri hodiny denne a jednu hodinu si nechal na iné úlohy.

Aktualizované užitočné zaťaženie vzorky Contours

[{

"minutes":900.0,

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

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

}]

Toto je priradenie po spustení rozhrania API Update Contour Schedule.

  Úloha Počiatočný dátum Konečný dátum Množstvo 13. 6. 2022 14. 6. 2022 15. 6. 2022 16. 6. 2022 17. 6. 2022
9-1 pracovník T1 13. 6. 2022 17. 6. 2022 15 3 3 3 3 3

Vzorový scenár

V tomto scenári vytvoríte projekt, člena tímu, štyri úlohy a dve priradenia zdrojov. Ďalej aktualizujete jednu úlohu, aktualizujete projekt, aktualizujete obrys priradenia zdrojov, odstránite jednu úlohu, odstránite jedno priradenie zdrojov a vytvoríte závislosť úlohy.

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....");

Ďalšie vzorky

#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