Verwenden von Projektplanungs-APIs zur Durchführung von Vorgängen mit Entitäten der Zeitplanung

Gilt für: Project Operations für Szenarien basierend auf vorrätigen/nicht vorrätigen Ressourcen, Lite-Bereitstellung – Abwicklung der Proforma-Rechnungsstellung.

Planungsentitäten

Projektzeitplan-APIs bieten die Möglichkeit, Vorgänge zum Erstellen, Aktualisieren und Löschen mit Terminplan-Entitäten auszuführen. Diese Entitäten werden über das Planungsmodul in Project für das Web verwaltet. Erstellen, Aktualisieren und Löschen von Vorgängen mit Planungsentitäten wurden in früheren Version von Dynamics 365 Project Operations eingeschränkt.

Die folgende Tabelle enthält eine vollständige Liste der Projektzeitplan-Entitäten.

Entitätsname Logischer Entitätsname
Project msdyn_project
Projektaufgabe msdyn_projecttask
Abhängigkeit der Projektaufgaben msdyn_projecttaskdependency
Ressourcenzuweisung msdyn_resourceassignment
Projekt-Bucket msdyn_projectbucket
Projektteammitglied msdyn_projectteam
Projektprüflisten msdyn_projectchecklist
Projektbeschriftung msdyn_projectlabel
Zu beschriftende Projektaufgabe msdyn_projecttasktolabel
Projektsprint msdyn_projectsprint

OperationSet

OperationSet ist ein Arbeitseinheitsmuster, das verwendet werden kann, wenn innerhalb einer Transaktion mehrere zeitplanbezogene Anforderungen verarbeitet werden müssen.

Projektzeitplan-APIs

Im Folgenden finden Sie eine Liste der aktuellen Projektzeitplan-APIs.

API Beschreibung des Dataflows
msdyn_CreateProjectV1 Diese API wird verwendet, um ein Projekt zu erstellen. Das Projekt und der Standardprojekt-Bucket werden sofort erstellt. Die Projekterstellung kann auch durch Hinzufügen einer Zeile zur Projekttabelle mithilfe von Standard-Dataverse-APIs erfolgen. Durch diesen Prozess wird kein Standard-Bucket für das Projekt erstellt, er bietet aber möglicherweise eine bessere Leistung.
msdyn_CreateTeamMemberV1 Diese API wird verwendet, um ein Teammitglied zu erstellen. Der Teammitgliedsdatensatz wird sofort erstellt. Die Teammitgliedererstellung kann auch durch Hinzufügen einer Zeile zur Projektteammitgliedertabelle mithilfe von Standard-Dataverse-APIs erfolgen.
msdyn_CreateOperationSetV1 Mit dieser API können mehrere Anforderungen geplant werden, die innerhalb einer Transaktion ausgeführt werden müssen.
msdyn_PssCreateV1 Diese API wird verwendet, um eine Entität zu erstellen. Die Entität kann eine der Projektplanungs-Entitäten sein, die den Vorgang des Erstellens unterstützen.
msdyn_PssCreateV2 Diese API wird verwendet, um eine Entität zu erstellen. Es funktioniert wie msdyn_PssCreateV1, aber mehrere Entitäten können in einer Aktion erstellt werden.
msdyn_PssUpdateV1 Diese API wird verwendet, um eine Entität zu aktualisieren. Die Entität kann eine beliebige Projektplanungs-Entität sein, die den Vorgang „Aktualisieren“ unterstützt.
msdyn_PssUpdateV2 Diese API wird verwendet, um Entitäten zu aktualisieren. Es funktioniert wie msdyn_PssUpdateV1, aber mehrere Entitäten können in einer Aktion aktualisiert werden.
msdyn_PssDeleteV1 Diese API wird verwendet, um eine Entität zu löschen. Die Entität kann eine beliebige Projektplanungs-Entität sein, die den Vorgang „Löschen“ unterstützt.
msdyn_PssDeleteV2 Diese API wird verwendet, um Entitäten zu löschen. Es funktioniert wie msdyn_PssDeleteV1, aber mehrere Entitäten können in einer Aktion gelöscht werden.
msdyn_ExecuteOperationSetV1 Diese API wird verwendet, um alle Operationen innerhalb des angegebenen Operationssatzes auszuführen.
msdyn_PssUpdateResourceAssignmentV1 Diese API wird verwendet, um eine geplante Arbeitskontur der Ressourcenzuweisung zu aktualisieren.

Verwenden von Projektzeitplan-APIs mit OperationSet

Da Datensätze sowohl mit CreateProjectV1 als auch CreateTeamMemberV1 sofort erstellt werden, können diese APIs nicht direkt in OperationSet verwendet werden. Sie können sie jedoch verwenden, um die erforderlichen Datensätze zu erstellen, ein OperationSet zu erstellen und dann diese vorab erstellten Datensätze in OperationSet zu verwenden.

Unterstützte Vorgänge

Planungsentität Erstellen Aktualisieren Delete Wichtige Überlegungen
Projektaufgabe Ja Ja Ja Die Felder Fortschritt, EffortCompleted und EffortRemaining können in Project for the Web bearbeitet werden, aber sie können nicht in Project Operations bearbeitet werden.
Abhängigkeit der Projektaufgaben Ja Nein Ja Abhängigkeitsdatensätze für Projektaufgaben werden nicht aktualisiert. Stattdessen kann ein alter Datensatz gelöscht und ein neuer Datensatz erstellt werden.
Ressourcenzuweisung Ja Ja* Ja Vorgänge mit den folgenden Feldern werden nicht unterstützt: BookableResourceID, Aufwand, EffortCompleted, EffortRemaining und PlannedWork.
Projekt-Bucket Ja Ja Ja Der Standard-Bucket wird mithilfe der CreateProjectV1-API erstellt. Unterstützung für das Erstellen und Löschen von Projekt-Buckets wurde in Update Release 16 hinzugefügt.
Projektteammitglied Ja Ja Ja Verwenden Sie für den Erstellungsvorgang die CreateTeamMemberV1-API.
Project Ja Ja Vorgänge mit den folgenden Feldern werden nicht unterstützt: StateCode, BulkGenerationStatus, GlobalRevisionToken, CalendarID, Aufwand, EffortCompleted, EffortRemaining, Fortschritt, Fertigstellen, TaskEarliestStart und Dauer.
Projektprüflisten Ja Ja Ja
Projektbeschriftung Nein Ja Nein Etikettennamen können geändert werden. Diese Funktion ist nur für Project for the Web verfügbar. Beschriftungen werden erstellt, wenn Sie zum ersten Mal ein Projekt öffnen.
Zu beschriftende Projektaufgabe Ja Nein Ja Diese Funktion ist nur für Project for the Web verfügbar.
Projektsprint Ja Ja Ja Das Start-Feld muss ein Datum vor dem Fertig-Feld haben. Sprints für dasselbe Projekt dürfen sich nicht überschneiden. Diese Funktion ist nur für Project for the Web verfügbar.
Projektziel Ja Ja Ja Vorgänge mit den folgenden Feldern werden nicht unterstützt: DescriptionPlainText, TaskDisplayOrder
Projektaufgabe für Ziel Ja Keine Ja Vorgänge mit den folgenden Feldern werden nicht unterstützt: TaskDisplayOrder

*Ressourcen-Zuweisungsdatensätze werden nicht aktualisiert. Stattdessen kann der alte Datensatz gelöscht und ein neuer Datensatz erstellt werden. Eine separate API wurde bereitgestellt, um die Konturen der Ressourcenzuweisung zu aktualisieren.

Diese ID-Eigenschaft ist optional. Wenn sie bereitgestellt wird, versucht das System, sie zu verwenden, und löst eine Ausnahme aus, wenn sie nicht verwendet werden kann. Wenn sie nicht bereitgestellt wird, generiert das System sie.

Einschränkungen und bekannte Probleme

Das Folgende ist eine Liste von Einschränkungen und bekannten Problemen:

  • Project Schedule APIs können nur von Benutzern mit Microsoft Project-Lizenz verwendet werden. Sie können nicht verwendet werden von:

    • Anwendungsbenutzer
    • Systembenutzern
    • Integrationsbenutzern
    • Andere Benutzer, die nicht über die erforderliche Lizenz verfügen
  • Jeder OperationSet kann nur maximal 200 Operationen haben.

  • Jeder Benutzer kann nur maximal 10 offene OperationSets haben.

  • Project Operations unterstützt derzeit maximal 500 Gesamtaufgaben für ein Projekt.

  • Jeder Vorgang zum Aktualisieren der Ressourcenzuweisungskontur zählt als ein einzelner Vorgang.

  • Jede Liste aktualisierter Konturen kann maximal 100 Zeitscheiben enthalten.

  • OperationSet-Fehlerstatus und -Fehlerprotokolle sind derzeit nicht verfügbar.

  • Es gibt maximal 400 Sprints pro Projekt.

  • Grenzwerte und Beschränkungen von Projekten und Aufgaben.

  • Beschriftungen sind derzeit nur für Project for the Web verfügbar.

  • Beschriftungen werden erstellt, wenn Sie zum ersten Mal ein Projekt öffnen.

  • Es gibt maximal 10 Ziele pro Projekt.

  • Jede Aufgabe kann einmal in „Projektaufgabe für Ziel“ erscheinen.

Fehlerbehandlung

  • Um die aus den Operations-Sätzen generierten Fehler zu überprüfen, gehen Sie zu Einstellungen>Integration planen>Operationssätze.
  • Um vom Project Schedule Service generierte Fehler zu überprüfen, gehen Sie auf Einstellungen>Planungsintegration>PSS Fehlerprotokolle.

Bearbeiten der Konturen der Ressourcenzuweisung

Im Gegensatz zu allen anderen Projektplanungs-APIs, die eine Entität aktualisieren, ist die Ressourcenzuweisungskontur-API ausschließlich für Aktualisierungen eines einzelnen Felds, msdyn_plannedwork, auf einer einzelnen Entität, msydn_resourceassignment, verantwortlich.

Der gegebene Zeitplanmodus ist:

  • Feste Einheiten
  • Der Projektkalender ist auf montags, dienstags, donnerstags und freitags von 9:00 bis 17:00 Uhr (pazifische Zeit) festgelegt. (Mittwochs wird nicht gearbeitet.)
  • Der Ressourcenkalender ist von 9:00 bis 13:00 Uhr (pazifische Zeit) von montags bis freitags festgelegt.

Diese Aufgabe dauert eine Woche, vier Stunden pro Tag. Dies liegt daran, dass der Ressourcenkalender von 9-13:00 Uhr (pazifische Zeit) oder auf vier Stunden pro Tag festgelegt ist.

  Aufgabe Startdatum Enddatum Menge 13.6.2022 14.6.2022 15.6.2022 16.6.2022 17.6.2022
9-1 Arbeitskraft T1 13.6.2022 17.6.2022 20 4 4 4 4 4

Wenn Sie beispielsweise möchten, dass die Arbeitskraft diese Woche nur drei Stunden pro Tag arbeitet und eine Stunde für andere Aufgaben einplanen soll.

UpdatedContours-Beispiel-Nutzlast

[{

"minutes":900.0,

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

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

}]

Dies ist die Zuweisung, nachdem die Update Contour Schedule API ausgeführt wurde.

  Task Startdatum Enddatum Menge 13.6.2022 14.6.2022 15.6.2022 16.6.2022 17.6.2022
9-1 Arbeitskraft T1 13.6.2022 17.6.2022 15 3 3 3 3 3

Beispielszenario

In diesem Szenario erstellen Sie ein Projekt, ein Teammitglied, vier Aufgaben und zwei Ressourcenzuweisungen. Als Nächstes aktualisieren Sie eine Aufgabe, aktualisieren das Projekt, aktualisieren eine Ressourcenzuweisungskontur, löschen eine Aufgabe, löschen eine Ressourcenzuweisung und erstellen eine Aufgabenabhängigkeit.

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

Zusätzliche Beispiele

#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