Deli putem


Korišćenje API-ja za raspored projekata za izvođenje operacija sa entitetima raspoređivanja

Primenjuje se na: Projektne operacije za scenarije zasnovane na resursima/nenapukovane, Lite deployment - dogovor sa proforma fakturisanjem.

Entiteti za planiranje

API-je za planiranje projekta obezbeđuju mogućnost izvršavanja operacija kreiranja, ažuriranja i brisanja sa entitetima za planiranje. Tim entitetima se upravlja putem mehanizma za raspoređivanje u aplikaciji Project za veb. Operacije kreiranja, ažuriranja i brisanja sa entitetima za planiranje ograničene su u ranijim Dynamics 365 Project Operations izdanjima.

Sledeća tabela daje potpunu listu entiteta za raspored projekata.

Naziv entiteta Logičko ime entiteta
Project msdyn_project
Projektni zadatak msdyn_projecttask
Zavisnost projektnog zadatka msdyn_projecttaskdependency
Dodela resursa msdyn_resourceassignment
Kontejner projekta msdyn_projectbucket
Član projektnog tima msdyn_projectteam
Liste za proveru projekta msdyn_projectchecklist
Oznaka projekta msdyn_projectlabel
Zavisnost projektnog zadataka i oznake msdyn_projecttasktolabel
Sprint projekta msdyn_projectsprint

OperationSet

Entitet OperationSet je obrazac jedinice rada koji se može koristiti kada se u transakciji mora obraditi nekoliko zahteva koji utiču na raspored.

API rasporeda projekata

Sledi lista aktuelnih API-ja za raspored projekata.

API Opis
msdyn_CreateProjectV1 Ovaj API se koristi za kreiranje projekta. Projekat i podrazumevani kontejner projekta kreiraju se odmah. Kreiranje projekta se takođe može uraditi dodavanjem reda u tabelu projekata pomoću standardnih Dataverse API-ja. Ovaj proces neće kreirati podrazumevanu kofu za projekat, ali će možda imati bolje performanse.
msdyn_CreateTeamMemberV1 Ovaj API se koristi za kreiranje člana projektnog tima. Evidencija člana tima kreira se odmah. Kreiranje člana tima može se uraditi i dodavanjem reda u tabelu "Član projektnog tima" koristeći standardne Dataverse API-je.
msdyn_CreateOperationSetV1 Ovaj API se koristi za zakazivanje nekoliko zahteva koji se moraju izvršiti u okviru transakcije.
msdyn_PssCreateV1 Ovaj API se koristi za kreiranje entiteta. Entitet može biti bilo koji entitet raspoređivanja projekta koji podržava operaciju kreiranja.
msdyn_PssCreateV2 Ovaj API se koristi za kreiranje entiteta. Funkcioniše kao msdyn_PssCreateV1 , ali se u jednoj radnji može kreirati više entiteta.
msdyn_PssUpdateV1 Ovaj API se koristi za ažuriranje entiteta. Entitet može biti bilo koji entitet raspoređivanja projekta koji podržava operaciju ažuriranja.
msdyn_PssUpdateV2 Ovaj API se koristi za ažurirane entitete. Radi kao msdyn_PssUpdateV1 , ali se više entiteta može ažurirati u jednoj radnji.
msdyn_PssDeleteV1 Ovaj API se koristi za brisanje entiteta. Entitet može biti bilo koji entitet raspoređivanja projekta koji podržava operaciju brisanja.
msdyn_PssDeleteV2 Ovaj API se koristi za brisanje entiteta. Funkcioniše kao msdyn_PssDeleteV1 , ali se više entiteta može izbrisati u jednoj radnji.
msdyn_ExecuteOperationSetV1 Ovaj API se koristi za izvršavanje svih operacija unutar datog skupa operacija.
msdyn_PssUpdateResourceAssignmentV1 Ovaj API se koristi za ažuriranje konture planiranog rada dodele resursa.

Korišćenje API-ja sa planom projekta pomoću programa OperationSet

Pošto se zapisi kreiraju odmah za CreateProjectV1 i CreateTeamMemberV1 , ovi API-je ne mogu direktno da se koriste uprogramu OperationSet . Međutim, možete da ih koristite za kreiranje potrebnih zapisa, kreiranje operationSet-a , azatim korišćenje unapred kreiranih zapisa u programu OperationSet.

Podržane operacije

Entitet planiranja Kreiranje Ažuriranje Izbrisi Važna razmatranja
Projektni zadatak Da Da Da Polja Progres,EffortCompleted i EffortRemaining mogu da se uređuju u projektu za Web, ali se ne mogu uređivati u operacijama projekta.
Zavisnost projektnog zadatka Da No Da Zapisi zavisnosti projektnih zadataka se ne ažuriraju. Umesto toga, stari zapis može da se izbriše i može da se kreira novi zapis.
Dodela resursa Da Da* Da Operacije sa sledećim poljima nisu podržane: BookableResourceID,Effort,EffortCompleted,EffortRemaining i PlannedWork.
Kontejner projekta Da Da Da Podrazumevana kofa se kreira pomoću API-ja CreateProjectV1 . Podrška za kreiranje i brisanje kontejnera projekta je dodata u izdanju 16 ažuriranja.
Član projektnog tima Da Da Da Za operaciju kreiranja koristite API CreateTeamMemberV1 .
Project Da Da Operacije sa sledećim poljima nisu podržane: StateCode,BulkGenerationStatus,GlobalRevisionToken,CalendarID,Effort,EffortCompleted,EffortRemaining,Progress,Finish,TaskEarliestStart i Duration.
Liste za proveru projekta Da Da Da
Oznaka projekta No Da No Nazivi oznaka se mogu promeniti. Ova funkcija je dostupna samo za Projekat za Web. Oznake se kreiraju kada prvi put otvorite projekat.
Zavisnost projektnog zadataka i oznake Da No Da Ova funkcija je dostupna samo za Projekat za Web.
Sprint projekta Da Da Da Polje "Start " mora imati datum raniji od polja Završi . Sprintevi za isti projekat ne mogu da se preklapaju. Ova funkcija je dostupna samo za Projekat za Web.
Cilj projekta Da Da Da Operacije sa sledećim poljima nisu podržane: DescriptionPlainText, TaskDisplayOrder
Zavisnost projektnog zadataka i cilja Da No Da Operacije sa sledećim poljima nisu podržane: TaskDisplayOrder

* Zapisi dodeljivanje resursa se ne ažuriraju. Umesto toga, stari zapis može da se izbriše i može da se kreira novi zapis. Obezbeđen je poseban API za ažuriranje kontura dodele resursa.

Svojstvo ID je opcionalno. Ako je obezbeđen, sistem pokušava da ga koristi i baca izuzetak ako ne može da se koristi. Ako nije obezbeđen, sistem ga generiše.

Ograničenja i poznati problemi

Sledi lista ograničenja i poznatih problema:

  • API-je za plan projekta mogu da koriste samo korisnici sa licencom za Microsoft Project. Ne mogu ih koristiti:

    • Korisnici aplikacije
    • Korisnici sistema
    • Korisnici integracije
    • Ostali korisnici koji nemaju potrebnu licencu
  • Svaki OperationSet može da ima najviše 200 operacija.

  • Svaki korisnik može imati najviše 10 otvorenih OperationSets .

  • Project Operations trenutno podržava maksimalno 1000 ukupnih zadataka na projektu.

  • Svaka operacija ažuriranja konture dodele resursa se računa kao jedna operacija.

  • Svaka lista ažuriranih kontura može da sadrži najviše 100 vremenskih isečaka.

  • Status otkazivanja programa OperationSet i evidencije otkazivanja trenutno nisu dostupni.

  • Postoji najviše 400 sprinta po projektu.

  • Ograničenja i granice za projekte i zadatke.

  • Oznake su trenutno dostupne samo za Project for the Web.

  • Oznake se kreiraju kada prvi put otvorite projekat.

  • Ima najviše 10 golova po projektu.

  • Svaki zadatak se može jednom pojaviti u projektnog zadatka cilju.

Rukovanje greškama

  • Da biste pregledali greške generisane iz skupova operacija, idite na skupove operacija integracije>>sa postavkama.
  • Da biste pregledali greške generisane iz usluge projektnog rasporeda, idite na evidencije>OS grešaka>za planiranje postavki.

Uređivanje kontura dodele resursa

Za razliku od svih drugih API-ja za planiranje koji ažuriraju entitet, API za konturu dodele resursa je isključivo odgovoran za ažuriranja jednog polja, msdyn_plannedwork, na jednim entitetom, msydn_resourceassignment.

Dati režim rasporeda je:

  • fiksne jedinice.
  • Kalendar projekta je od 9:00 do 17:00 časova (po pacifičkom vremenu) u ponedeljak, utorak, četvrtak i petak. (Sredom nema posla.)
  • Kalendar resursa je od 9:00 do 1:00 popodne (pacifičko vreme) od ponedeljka do petka.

Ovaj zadatak je za nedelju dana, četiri sata dnevno. To je zato što je kalendar resursa od 9:00 do 1:00 popodne (pacifičko vreme), odnosno četiri sata dnevno.

  Zadatak Datum početka Datum završetka Količina 13.6.2022. 14.6.2022. 15.6.2022. 16.6.2022. 17.6.2022.
9-1 radnik T1 13.6.2022. 17.6.2022. 20 4 4 4 4 4

Na primer, ako želite da radnik radi samo tri sata svakog dana ove sedmice i omogućite jedan sat za druge zadatke.

AžuriranoContours uzorak tovara

[{

"minutes":900.0,

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

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

}]

Ovo je dodela nakon što se pokrene API za ažuriranje rasporeda kontura.

  Zadatak Datum početka Datum završetka Količina 13.6.2022. 14.6.2022. 15.6.2022. 16.6.2022. 17.6.2022.
9-1 radnik T1 13.6.2022. 17.6.2022. 15 3 3 3 3 3

Uzorak scenarija

U ovom scenariju kreirate projekat, člana tima, četiri zadatka i dve dodele resursa. Zatim ažurirate jedan zadatak, ažurirate projekat, ažurirate konturu dodele resursa, izbrišete jedan zadatak, izbrišete jednu dodelu resursa i kreirate zavisnost od zadatka.

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

Dodatni uzorci

#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