Uporaba API-jev razporejanja projektov za izvajanje postopkov s pomočjo entitet razporejanja

Velja za: Projektne operacije za scenarije, ki temeljijo na virih/nezaloženih, poenostavljena uvedba – obravnava predračunov.

Entitete za razporejanje

API-ji za načrtovanje projekta omogočajo izvajanje operacij ustvarjanja, posodabljanja in brisanja z entitetami za načrtovanje. Te entitete upravljamo prek mehanizma za razporejanje v projektu za splet. Operacije ustvarjanja, posodabljanja in brisanja z entitetami razporejanja so bile omejene v prejšnjih Dynamics 365 Project Operations izdajah.

Naslednja tabela vključuje celoten seznam entitet razporejanja projektov.

Ime entitete Logično ime entitete
Project msdyn_project
Projektno opravilo msdyn_projecttask
Odvisnost projektnega opravila msdyn_projecttaskdependency
Dodelitev vira msdyn_resourceassignment
Vedro projekta msdyn_projectbucket
Član projektne ekipe msdyn_projectteam
Kontrolni seznami projekta msdyn_projectchecklist
Oznaka projekta msdyn_projectlabel
Oznaka dodelitve opravila projektu msdyn_projecttasktolabel
Projektni sprint msdyn_projectsprint

OperationSet

OperationSet je vzorec enote dela, ki se lahko uporablja, kadar je treba v transakciji obdelati več zahtev, ki vplivajo na urnik.

API-ji urnika projekta

Naslednji seznam vsebuje trenutne API-je razporejanja projektov.

API Description
msdyn_CreateProjectV1 Ta API se uporablja za ustvarjanje projekta. Projekt in privzeto vedro projekta se ustvarita takoj. Projekt lahko ustvarite tudi tako, da dodate vrstico v projektno tabelo z uporabo standardnih Dataverse API-jev. Ta postopek ne bo ustvaril privzetega vedra za projekt, vendar bo morda imel boljšo zmogljivost.
msdyn_CreateTeamMemberV1 Ta API se uporablja za ustvarjanje člana ekipe projekta. Zapis člana ekipe se ustvari takoj. Člana ekipe lahko ustvarite tudi tako, da dodate vrstico v tabelo Član projektne skupine s standardnimi Dataverse API-ji.
msdyn_CreateOperationSetV1 Ta API se uporablja za razporejanje več zahtev, ki jih je treba izvesti znotraj transakcije.
msdyn_PssCreateV1 Ta API se uporablja za ustvarjanje entitete. Lahko gre za katerokoli entiteto razporejanja projektov, ki podpira postopek ustvarjanja.
msdyn_PssCreateV2 Ta API se uporablja za ustvarjanje entitete. Deluje kot msdyn_PssCreateV1, vendar je mogoče z enim dejanjem ustvariti več entitet.
msdyn_PssUpdateV1 Ta API se uporablja za posodobitev entitete. Lahko gre za katerokoli entiteto razporejanja projektov, ki podpira postopek posodabljanja.
msdyn_PssUpdateV2 Ta API se uporablja za posodobljene entitete. Deluje kot msdyn_PssUpdateV1, vendar je mogoče z enim dejanjem posodobiti več entitet.
msdyn_PssDeleteV1 Ta API se uporablja za brisanje entitete. Lahko gre za katerokoli entiteto razporejanja projektov, ki podpira postopek brisanja.
msdyn_PssDeleteV2 Ta API se uporablja za brisanje entitet. Deluje kot msdyn_PssDeleteV1, vendar je mogoče z enim dejanjem izbrisati več entitet.
msdyn_ExecuteOperationSetV1 Ta API se uporablja za izvajanje vseh postopkov znotraj danega nabora postopkov.
msdyn_PssUpdateResourceAssignmentV1 Ta API se uporablja za posodobitev načrtovanega dela krivulj dodelitve virov.

Uporaba API-jev razporeda projekta z OperationSet

Ker so zapisi ustvarjeni takoj za CreateProjectV1 in CreateTeamMemberV1, teh API-jev ni mogoče uporabiti neposredno v OperationSet. Lahko pa jih uporabite za ustvarjanje zahtevanih zapisov, ustvarite OperationSet in nato uporabite vnaprej ustvarjene zapise v OperationSet.

Podprte operacije

Entiteta za razporejanje Ustvari Posodabljanje Delete Pomembni premisleki
Projektno opravilo Da Da Da Polja Progress, EffortCompleted in EffortRemaining lahko urejate v Project za splet, vendar jih ni mogoče urejati v Project Operations.
Odvisnost projektnega opravila Da No Da Zapisi odvisnosti od projektne naloge se ne posodabljajo. Namesto tega lahko stari zapis izbrišete in ustvarite novega.
Dodelitev vira Da Da* Da Operacije z naslednjimi polji niso podprte: BookableResourceID, Effort, EffortCompleted , Preostali trud in Načrtovano delo.
Vedro projekta Da Da Da Privzeto vedro je ustvarjeno z uporabo CreateProjectV1 API-ja. Podpora za ustvarjanje in brisanje projektnih veder je bila dodana v izdaji posodobitve 16.
Član projektne ekipe Da Da Da Za operacijo ustvarjanja uporabite CreateTeamMemberV1 API.
Projekt Da Da Operacije z naslednjimi polji niso podprte: StateCode, BulkGenerationStatus, GlobalRevisionToken , CalendarID, Effort, EffortCompleted, Preostali trud, Napredek, Končaj, TaskEarliestStartin Duration.
Kontrolni seznami projekta Da Da Da
Oznaka projekta No Da No Imena oznak je mogoče spremeniti. Ta funkcija je na voljo samo za Project za splet. Oznake se ustvarijo, ko prvič odprete projekt.
Oznaka dodelitve opravila projektu Da No Da Ta funkcija je na voljo samo za Project za splet.
Projektni sprint Da Da Da Polje Začetek mora imeti datum pred poljem Konec . Šprinti za isti projekt se ne morejo prekrivati. Ta funkcija je na voljo samo za Project za splet.
Cilj projekta Da Da Da Operacije z naslednjimi polji niso podprte: DescriptionPlainText, TaskDisplayOrder
Projektno opravilo v cilj Da No Da Operacije z naslednjimi polji niso podprte: TaskDisplayOrder

* Zapisi o dodelitvi virov niso posodobljeni. Namesto tega lahko stari zapis izbrišete in ustvarite novega. Na voljo je ločen API za posodobitev krivulj dodelitve virov.

Lastnost ID ni obvezna. Če je na voljo, ga sistem poskuša uporabiti in vrže izjemo, če ga ni mogoče uporabiti. Če ni na voljo, ga sistem ustvari.

Omejitve in znane težave

Sledi seznam omejitev in znanih težav:

  • API-je za urnik projekta lahko uporabljajo samo uporabniki z licenco Microsoft Project. Ne morejo jih uporabljati:

    • Uporabniki aplikacij
    • Uporabniki sistema
    • Uporabniki integracije
    • Drugi uporabniki, ki nimajo zahtevane licence
  • Vsak OperationSet ima lahko največ 200 operacij.

  • Vsak uporabnik ima lahko največ 10 odprtih OperationSets.

  • Project Operations trenutno podpira največ 500 skupnih opravil na projektu.

  • Vsak postopek krivulj dodelitve virov velja za en sam postopek.

  • Vsak seznam posodobljenih krivulj lahko vsebuje največ 100 časovnih rezin.

  • Stanje napake OperationSet in dnevniki napak trenutno niso na voljo.

  • Na projekt je največ 400 sprintov.

  • Omejitve in meje projektov in nalog.

  • Oznake so trenutno na voljo samo v storitvi Project for the Web.

  • Oznake se ustvarijo, ko prvič odprete projekt.

  • Na projekt je največ 10 ciljev.

  • Vsako opravilo se lahko enkrat prikaže v projektu Project Task to Goal.

Obravnava napak

  • Za pregled napak, ustvarjenih iz naborov operacij, pojdite na Nastavitve>Integracija urnika>Nabori operacij.
  • Če želite pregledati napake, ustvarjene s storitvijo Project schedule Service, pojdite na Settings>Schedule Integration>PSS Error Dnevniki.

Urejanje obrisov dodelitve virov

Za razliko od vseh drugih API-jev za načrtovanje projektov, ki posodabljajo entiteto, je API za krivulje dodelitve virov odgovoren izključno za posodobitve enega polja, msdyn_plannedwork, v eni entiteti, msydn_resourceassignment.

Podani način načrtovanja je:

  • fiksne enote.
  • Koledar projekta je od 9:00 do 17:00 (pacifiški čas) ponedeljek, torek, četrtek in petek. (Ob sredah ni dela.)
  • Koledar virov je od 9.00 do 13.00 (po pacifiškem času) od ponedeljka do petka.

Ta dodelitev traja en teden, štiri ure na dan. To je zato, ker je koledar virov od 9.00 do 13.00 (po pacifiškem času) ali štiri ure na dan.

  opravilo, Datum začetka Datum konca Količina 13. 6. 2022 14. 6. 2022 15. 6. 2022 16. 6. 2022 17. 6. 2022
delavec 9–13 T1 13. 6. 2022 17. 6. 2022 20 4 4 4 4 4

Če na primer želite, da delavec ta teden vsak dan dela samo tri ure in ima eno uro za druga opravila.

Posodobljeni vzorčni tovor Contours

[{

"minutes":900.0,

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

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

}]

To je dodelitev po zagonu API-ja Urnik krivulj posodobitve.

  opravilo, Datum začetka Datum konca Količina 13. 6. 2022 14. 6. 2022 15. 6. 2022 16. 6. 2022 17. 6. 2022
delavec 9–13 T1 13. 6. 2022 17. 6. 2022 15 3 3 3 3 3

Vzorčni scenarij

V tem scenariju ustvarite projekt, člana ekipe, štiri naloge in dve dodelitvi virov. Nato posodobite eno opravilo, posodobite projekt, posodobite obris dodelitve vira, izbrišete eno opravilo, izbrišete eno dodelitev vira in ustvarite odvisnost od opravila.

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 vzorci

#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