Del via


Brug projektplanlægnings-API'er til at udføre handlinger med planlægningsobjekter

Gælder for: Project Operations for scenarier baseret på ressource/ikke-lager, lille udrulning - aftale til håndtering af proformafakturering.

Planlægningsobjekter

Projektplanlægnings-API'er gør det muligt at oprette, opdatere og slette handlinger med Planlægningsobjekter. Disse objekter administreres via planlægningsprogrammet i Projekt til internettet. Handlinger til oprettelse, opdatering og sletning med Planlægningsobjekter var i tidligere udgivelser til Dynamics 365 Project Operations begrænset.

Følgende tabel indeholder en fuldstændig liste over projektplanlægningsobjekterne.

Enhedsnavn Objektets logiske navn
Project msdyn_project
Projektopgave msdyn_projecttask
Projektopgaveafhængighed msdyn_projecttaskdependency
Ressourcetildeling msdyn_resourceassignment
Projektbucket msdyn_projectbucket
Medlem af projektteam msdyn_projectteam
Projekttjeklister msdyn_projectchecklist
Projektnavn msdyn_projectlabel
Projektopgave til navn msdyn_projecttasktolabel
Projektsprint msdyn_projectsprint

OperationSet

OperationSet er et arbejdsenhedsmønster, der kan bruges, når flere forespørgsler, der påvirker tidsplanen, skal behandles i en transaktion.

Projektplanlægnings-API'er

Følgende er en liste over aktuelle projektplanlægnings-API'er.

API Beskrivelse
msdyn_CreateProjectV1 Denne API bruges til at oprette et projekt. Projekt- og standardprojektet-bucket oprettes med det samme. Du kan også oprette et projekt ved at føje en række til projekttabellen ved hjælp af standard Dataverse-API'er. Denne proces skaber ikke en standardbucket for projektet, men den kan have en bedre ydeevne.
msdyn_CreateTeamMemberV1 Denne API bruges til at oprette et teammedlem. Teammedlemsposten oprettes med det samme. Du kan også oprette et teammedlem ved at føje en række til tabellen Medlem af projektteam ved hjælp af standard Dataverse-API'er.
msdyn_CreateOperationSetV1 Denne API kan bruges til at planlægge flere forespørgsler, der skal udføres i en transaktion.
msdyn_PssCreateV1 Denne API bruges til at oprette et objekt. Objektet kan være et hvilket som helst af de projektplanlægningsobjekter, der understøtter oprettelseshandlingen.
msdyn_PssCreateV2 Denne API bruges til at oprette et objekt. Det fungerer som msdyn_PssCreateV1, men der kan oprettes flere objekter i én handling.
msdyn_PssUpdateV1 Denne API bruges til at opdatere et objekt. Objektet kan være et hvilket som helst af de projektplanlægningsobjekter, der understøtter opdateringshandlingen.
msdyn_PssUpdateV2 Denne API bruges til opdaterede objekter. Det fungerer som msdyn_PssUpdateV1, men der kan opdateres flere objekter i én handling.
msdyn_PssDeleteV1 Denne API bruges til at slette et objekt. Objektet kan være et hvilket som helst af de projektplanlægningsobjekter, der understøtter slettehandlingen.
msdyn_PssDeleteV2 Denne API bruges til at slette objekter. Det fungerer som msdyn_PssDeleteV1, men der kan slettes flere objekter i én handling.
msdyn_ExecuteOperationSetV1 Denne API bruges til at udføre alle handlinger i det givne operationssæt.
msdyn_PssUpdateResourceAssignmentV1 Denne API bruges til at opdatere planlagt arbejdsprofil for ressourcetildeling.

Brug af projektplanlægnings-API'er med handlingssæt

Da poster oprettes med det samme til både CreateProjectV1 og CreateTeamMemberV1, kan disse API'er ikke bruges direkte i OperationSet. Du kan dog bruge dem til at oprette de krævede poster, oprette et OperationSet og derefter bruge de forudoprettede poster i OperationSet.

Understøttede handlinger

Planlægningsobjekt Opret Opdater Delete Vigtige overvejelser
Projektopgave Ja Ja Ja Felterne Fremgang, EffortCompleted og EffortRemaining kan redigeres i Project for the Web, men de kan ikke redigeres i Project Operations.
Projektopgaveafhængighed Ja Nej Ja Projektopgaveafhængighedsposter opdateres ikke. I stedet kan en gammel post slettes, og der kan oprettes en ny post.
Ressourcetildeling Ja Ja* Ja Handlinger med følgende felter understøttes ikke: BookableResourceID, Indsats, EffortCompleted, EffortRemaining og PlannedWork.
Projektbucket Ja Ja Ja Standardplaceringen oprettes ved hjælp af API'en CreateProjectV1. I frigivelsen af opdatering 16 blev der tilføjet support til oprettelse og sletning af projektbucket.
Projektteammedlem Ja Ja Ja Brug API'en CreateTeamMemberV1 til oprettelseshandlingen.
Project Ja Ja Nej Handlinger med følgende felter understøttes ikke: StateCode, BulkStatus, GlobalRevisionToken, CalendarID, Indsats, EffortCompleted, EffortRemaining, Status, Afsluttet, TaskEarliestStart og Varighed.
Projekttjeklister Ja Ja Ja
Projektnavn Nej Ja Nej Etiketnavne kan ændres. Denne funktion er kun tilgængelig for Project for the Web. Etiketter oprettes, første gang du åbner et projekt.
Projektopgave til navn Ja Nej Ja Denne funktion er kun tilgængelig for Project for the Web.
Projektsprint Ja Ja Ja Feltet Start skal have en dato, der er tidligere end feltet Afslut. Sprints for det samme projekt kan ikke overlappe hinanden. Denne funktion er kun tilgængelig for Project for the Web.
Projektmål Ja Ja Ja Handlinger med følgende felter understøttes ikke: DescriptionPlainText, TaskDisplayOrder
Projektopgave til mål Ja Nej Ja Handlinger med følgende felter understøttes ikke: TaskDisplayOrder

* Ressourcetildelingsposter opdateres ikke. I stedet kan den gamle post slettes, og der kan oprettes en ny post. Der leveres en separat API til opdatering af ressourcetildelingskonturer.

Id-egenskaben er valgfri. Hvis egenskaben ID oplyses, forsøger systemet at bruge den, og der udløses en undtagelse, hvis den ikke kan bruges. Hvis den ikke er angivet, oprettes den af systemet.

Begrænsninger og kendte problemer

Følgende liste viser begrænsninger og kendte problemer:

  • Projektplanlægnings-Api'er kan kun bruges af Brugere med Microsoft Project-licens. De kan ikke bruges af:

    • Programbrugere
    • Systembrugere
    • Integrationsbrugere
    • Andre brugere, der ikke har den påkrævede licens
  • Hvert OperationSet kan kun have op til 200 handlinger.

  • Hver bruger kan kun have op til 10 åbne OperationSet.

  • Hver opdateringshandling for profiler for ressourcetildelinger tæller som en enkelt handling.

  • Hver liste over opdaterede profiler kan indeholde op til 100 tidsudsnit.

  • Status for fejl i OperationSet og fejllogfiler er ikke tilgængelige i øjeblikket.

  • Der må højst være 400 sprints pr. projekt.

  • Begrænsninger og grænser for projekter og opgaver.

Fejlhåndtering

  • Hvis du vil gennemgå de fejl, der er genereret i operationssæt, skal du gå til Indstillinger>Integration af planlægning>Operationssæt.
  • Hvis du vil gennemgå de fejl, der er genereret fra projektplanlægningstjenesten, skal du gå til Indstillinger>Planlægning af integration>PSS-fejllogfiler.

Redigering af profiler for ressourcetildeling

I modsætning til alle andre API'er for projektplanlægning, der opdaterer et objekt, er API'en for profiler for ressourcetildelinger eneansvarlig for opdateringer af et enkelt felt, msdyn_plannedwork, på et enkelt objekt, msydn_resourceassignment.

Den angivne planlægningstilstand er:

  • faste enheder.
  • Projektkalenderen er fra 9:00 til 17:00 (Pacific Time) mandag, tirsdag, torsdag og fredag. (Der er intet arbejde om onsdagen.)
  • Ressourcekalenderen er fra kl. 9:00 til 13:00 (Pacific Time) mandag til fredag.

Denne tildeling er for én uge, fire timer om dagen, fordi ressourcekalenderen er fra 9:00 til 13:00, eller fire timer om dagen.

  Opgave Startdato Slutdato Quantity 13-06-2022 14-06-2022 15-06-2022 16-06-2022 17-06-2022
9-1 arbejder T1 13-06-2022 17-06-2022 20 4 4 4 4 4

Hvis arbejderen f.eks. kun skal arbejde tre timer hver dag i denne uge, og der skal frigives én time til andre opgaver.

UpdatedContours-eksempelnyttedata

[{

"minutes":900.0,

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

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

}]

Dette er tildelingen, når API'en til opdatering af profilplanlægning er kørt.

  Opgave Startdato Slutdato Quantity 13-06-2022 14-06-2022 15-06-2022 16-06-2022 17-06-2022
9-1 arbejder T1 13-06-2022 17-06-2022 15 3 3 3 3 3

Eksempelscenarie

I dette scenario skal du oprette et projekt, et teammedlem, fire opgaver og to ressourcetildelinger. Derefter skal du opdatere én opgave, opdatere projektet, opdatere en ressourcetildelingskontur, slette én opgave, slette én ressourcetildeling og oprette en opgaveafhængighed.

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

Yderligere eksempler

#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