API's voor projectplanning gebruiken om bewerkingen uit te voeren met planningsentiteiten
Van toepassing op: Project Operations voor scenario's op basis van resources/niet-voorradige artikelen, Lite-implementatie - van deal tot pro-formafacturering.
Planningsentiteiten
API's voor projectplanning bieden de mogelijkheid om bewerkingen voor het maken, bijwerken en verwijderen uit te voeren met Planningsentiteiten. Deze entiteiten worden beheerd via de planningsengine in Project voor het web. Bewerkingen voor maken, bijwerken en verwijderen met planningsentiteiten waren beperkt in eerdere Dynamics 365 Project Operations-releases.
De volgende tabel bevat een volledige lijst met de entiteiten voor projectplanning.
Naam van entiteit | Logische naam van entiteit |
---|---|
Project | msdyn_project |
Projecttaak | msdyn_projecttask |
Afhankelijkheid van projecttaken | msdyn_projecttaskdependency |
Resourcetoewijzing | msdyn_resourceassignment |
Projectbucket | msdyn_projectbucket |
Projectteamlid | msdyn_projectteam |
Projectchecklijsten | msdyn_projectchecklist |
Projectlabel | msdyn_projectlabel |
Te labelen projecttaak | msdyn_projecttasktolabel |
Projectsprint | msdyn_projectsprint |
OperationSet
OperationSet is een werkeenheidspatroon dat kan worden gebruikt wanneer meerdere verzoeken die betrekking hebben op de planning binnen een transactie moeten worden verwerkt.
API´s voor projectplanning
Hierna volgt een lijst met huidige API's voor projectplanning.
API | Omschrijving |
---|---|
msdyn_CreateProjectV1 | Deze API wordt gebruikt om een project aan te maken. Het project en de standaard projectbucket worden onmiddellijk gemaakt. U kunt ook projecten maken door een rij aan de projecttabel toe te voegen met behulp van standaard Dataverse-API's. Dit proces maakt geen standaardbucket voor het project, maar kan wel betere prestaties opleveren. |
msdyn_CreateTeamMemberV1 | Deze API wordt gebruikt om een projectteamlid aan te maken. De teamlidrecord wordt onmiddellijk gemaakt. U kunt ook teamleden maken door een rij aan de tabel Projectteamleden toe te voegen met behulp van standaard Dataverse-API's. |
msdyn_CreateOperationSetV1 | Deze API wordt gebruikt om verschillende verzoeken te plannen die binnen een transactie moeten worden uitgevoerd. |
msdyn_PssCreateV1 | Deze API wordt gebruikt om een entiteit te maken. De entiteit kan een van de projectplanningsentiteiten zijn die de bewerking voor maken ondersteunen. |
msdyn_PssCreateV2 | Deze API wordt gebruikt om een entiteit te maken. Werkt net als msdyn_PssCreateV1, maar nu kunnen er meerdere entiteiten in één actie worden gemaakt. |
msdyn_PssUpdateV1 | Deze API wordt gebruikt om een entiteit bij te werken. De entiteit kan een van de projectplanningsentiteiten zijn die de bewerking voor bijwerken ondersteunen. |
msdyn_PssUpdateV2 | Deze API wordt gebruikt voor het bijwerken van entiteiten. Werkt net als msdyn_PssUpdateV1, maar nu kunnen er meerdere entiteiten in één actie worden bijgewerkt. |
msdyn_PssDeleteV1 | Deze API wordt gebruikt om een entiteit te verwijderen. De entiteit kan een van de projectplanningsentiteiten zijn die de bewerking voor verwijderen ondersteunen. |
msdyn_PssDeleteV2 | Deze API wordt gebruikt voor het verwijderen van entiteiten. Werkt net als msdyn_PssDeleteV1, maar nu kunnen er meerdere entiteiten in één actie worden verwijderd. |
msdyn_ExecuteOperationSetV1 | Deze API wordt gebruikt om alle bewerkingen binnen de opgegeven bewerkingsset uit te voeren |
msdyn_PssUpdateResourceAssignmentV1 | Deze API wordt gebruikt om een geplande werkcontour van een resourcetoewijzing bij te werken. |
API's voor projectplanning gebruiken met OperationSet
Omdat records direct worden gemaakt voor zowel CreateProjectV1 als CreateTeamMemberV1, kunnen deze API's niet rechtstreeks in de OperationSet worden gebruikt. U kunt ze echter gebruiken om de vereiste records te maken, een OperationSet te maken en vervolgens de vooraf gemaakte records in de OperationSet te gebruiken.
Ondersteunde bewerkingen
Planningsentiteit | Maken | Bijwerken | Delete | Belangrijke aandachtspunten |
---|---|---|---|---|
Projecttaak | Ja | Ja | Ja | De velden Progress, EffortCompleted en EffortRemaining kunnen worden bewerkt in Project for the Web, maar niet in Project Operations. |
Afhankelijkheid van projecttaken | Ja | Nee | Ja | Records voor de afhankelijkheid van projecttaken worden niet bijgewerkt. In plaats daarvan kan een oude record worden verwijderd en kan een nieuwe record worden gemaakt. |
Resourcetoewijzing | Ja | Ja* | Ja | Bewerkingen met de volgende velden worden niet ondersteund: BookableResourceID, Effort, EffortCompleted, EffortRemaining en PlannedWork. |
Projectbucket | Ja | Ja | Ja | De standaardbucket wordt gemaakt met behulp van de API CreateprojectV1. Ondersteuning voor het maken en verwijderen van projectbuckets is toegevoegd in Update Release 16. |
Projectteamlid | Ja | Ja | Ja | Gebruik voor de maakbewerking de API CreateTeamMemberV1. |
Project | Ja | Ja | No | Bewerkingen met de volgende velden worden niet ondersteund: StateCode, BulkGenerationStatus, GlobalRevisionToken, CalendarID, Effort, EffortCompleted, EffortRemaining, Progress, Finish, TaskEarliestStart en Duration. |
Projectchecklijsten | Ja | Ja | Ja | |
Projectlabel | Nee | Ja | Nee | Labelnamen kunnen worden gewijzigd. Deze functie is alleen beschikbaar voor Project for the Web. Labels worden gemaakt wanneer u voor het eerst een project opent. |
Te labelen projecttaak | Ja | Nee | Ja | Deze functie is alleen beschikbaar voor Project for the Web. |
Projectsprint | Ja | Ja | Ja | De datum in he veld Starten moet voor de datum in het veld Voltooien vallen. Sprints voor hetzelfde project mogen elkaar niet overlappen. Deze functie is alleen beschikbaar voor Project for the Web. |
Projectdoel | Ja | Ja | Ja | Bewerkingen met de volgende velden worden niet ondersteund: DescriptionPlainText, TaskDisplayOrder |
Projecttaak voor doel | Ja | Geen | Ja | Bewerkingen met de volgende velden worden niet ondersteund: TaskDisplayOrder |
* Records voor resourcetoewijzingen worden niet bijgewerkt. In plaats daarvan kan de oude record worden verwijderd en kan een nieuwe record worden gemaakt. Er is een aparte API beschikbaar om Resource Assignment-contouren bij te werken.
De id-eigenschap is optioneel. Als de eigenschap Id wordt opgegeven, probeert het systeem deze te gebruiken en wordt er een uitzondering gegenereerd als deze niet kan worden gebruikt. Als deze niet wordt opgegeven, genereert het systeem deze.
Beperkingen en bekende problemen
Hieronder volgt een lijst met beperkingen en bekende problemen:
API's voor projectplanning kunnen alleen worden gebruikt door Gebruikers met Microsoft Project-licentie. Ze kunnen niet worden gebruikt door:
- Gebruikers van de toepassing
- Systeemgebruikers
- Integration-gebruikers
- Andere gebruikers die niet over de vereiste licentie beschikken
Elke OperationSet kan maximaal 200 bewerkingen omvatten.
Elke gebruiker kan maximaal tien open OperationSets hebben.
Elke bewerking om contouren van resourcetoewijzingen bij te werken, telt als één bewerking.
Elke lijst met bijgewerkte contouren kan maximaal 100 tijdssecties bevatten.
Momenteel zijn geen foutstatus en foutenlogboeken beschikbaar voor OperationSet.
Er geldt een maximum van 400 sprints per project.
Foutafhandeling
- Ga naar Instellingen>Integratie plannen>Bewerkingssets om fouten te bekijken die zijn gegenereerd door de bewerkingssets.
- Als u fouten wilt bekijken die zijn gegenereerd via de projectplanningsservice, gaat u naar Instellingen>Integratie plannen>PSS-foutenlogboeken.
Contouren voor resourcetoewijzing bewerken
In tegenstelling tot alle andere API's voor projectplanning die een entiteit bijwerken, is de API voor contouren voor resourcetoewijzing als enige verantwoordelijk voor updates van één veld, msdyn_plannedwork, van één entiteit, msydn_resourceassignment.
De gegeven planningsmodus is:
- vaste eenheden.
- De projectagenda loopt van 09.00 tot 17.00 uur (Pacific-tijd) maandag, dinsdag, donderdag en vrijdag. (Er wordt niet gewerkt op woensdag.)
- De recource-agenda loopt van 09.00 tot 13.00 uur (Pacific-tijd) van maandag tot en met vrijdag.
Deze toewijzing is voor één week, vier uur per dag, omdat de resourcekalender loopt van 9:00 tot 13:00 (Pacific Time), of vier uur per dag.
Taak | Begindatum | Einddatum | Aantal | 13-06-2022 | 14-06-2022 | 15-06-2022 | 16-06-2022 | 17-06-2022 | |
---|---|---|---|---|---|---|---|---|---|
Werknemer van 9-1 | T1 | 13-06-2022 | 17-06-2022 | 20 | 4 | 4 | 4 | 4 | 4 |
Als u bijvoorbeeld wilt dat de werknemer deze week slechts drie uur per dag werkt en één uur voor andere taken reserveert.
Voorbeeldnettolading van UpdatedContours
[{
"minutes":900.0,
"start":"2022-06-13T00:00:00-07:00",
"end":"2022-06-18T00:00:00-07:00"
}]
Dit is de toewijzing nadat de API voor het bijwerken van de contourplanning is uitgevoerd.
Opdracht | Begindatum | Einddatum | Aantal | 13-06-2022 | 14-06-2022 | 15-06-2022 | 16-06-2022 | 17-06-2022 | |
---|---|---|---|---|---|---|---|---|---|
Werknemer van 9-1 | T1 | 13-06-2022 | 17-06-2022 | 15 | 3 | 3 | 3 | 3 | 3 |
Voorbeeldscenario
In dit scenario maakt u een project, een teamlid, vier taken en twee resourcetoewijzingen. Vervolgens werkt u één taak bij, werkt u het project bij, werkt u een contour voor een resourcetoewijzing bij, verwijdert u één taak, verwijdert u één resourcetoewijzing en maakt u een taakafhankelijkheid.
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....");
Aanvullende voorbeelden
#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