Nota
L'accés a aquesta pàgina requereix autorització. Pots provar d'iniciar sessió o canviar de directori.
L'accés a aquesta pàgina requereix autorització. Pots provar de canviar directoris.
S'aplica a: Operacions de projecte integrades amb ERP, Project Operations Core.
Entitats de planificació
Les API de planificació de projectes proporcionen la possibilitat de realitzar operacions de creació, actualització i supressió amb entitats de planificació. Aquestes entitats s'administren mitjançant el motor de Planificació del Projecte for the web. Les operacions de creació, actualització i supressió amb entitats de planificació estaven restringides en versions anteriors Dynamics 365 Project Operations .
A la taula següent es proporciona una llista completa de les entitats de planificació de projectes.
| Nom de l’entitat | Nom lògic de l’entitat |
|---|---|
| Projecte | msdyn_project |
| Tasca del projecte | msdyn_projecttask |
| Dependència de les tasques del projecte | msdyn_projecttaskdependency |
| Assignació de recursos | msdyn_resourceassignment |
| Dipòsit de projecte | msdyn_projectbucket |
| Membre de l'equip del projecte | msdyn_projectteam |
| Llistes de comprovació del projecte | msdyn_projectchecklist |
| Etiqueta del projecte | msdyn_projectlabel |
| Tasca de projecte que s'ha d'etiqueta | msdyn_projecttasktolabel |
| Esprint del projecte | msdyn_projectsprint |
Conjunt d'operacions
OperationSet és un patró d'unitat de treball que es pot utilitzar quan diverses sol·licituds que afecten la planificació s'han de processar dins d'una transacció.
API de planificació de projectes
A continuació es mostra una llista de les API de planificació de projectes actuals.
| API | Descripció |
|---|---|
| msdyn_CreateProjectV1 | Aquesta API s'utilitza per crear un projecte. El projecte i el dipòsit per defecte del projecte es creen immediatament. La creació del projecte també es pot fer afegint una fila a la taula del projecte mitjançant API estàndard Dataverse . Aquest procés no crea un cub per defecte per al projecte, però pot tenir un millor rendiment. |
| msdyn_CreateTeamMemberV1 | Aquesta API s'utilitza per crear un membre de l'equip del projecte. El registre del membre de l'equip es crea immediatament. La creació de membres de l'equip també es pot fer afegint una fila a la taula Membre de l'equip del projecte mitjançant API estàndard Dataverse . |
| msdyn_CreateOperationSetV1 | Aquesta API s'utilitza per planificar diverses sol·licituds que s'han de fer dins d'una transacció. |
| msdyn_PssCreateV1 | Aquesta API s'utilitza per crear una entitat. L'entitat pot ser qualsevol de les entitats de planificació de projectes que admeten l'operació de creació. |
| msdyn_PssCreateV2 | Aquesta API s'utilitza per crear una entitat. Funciona com msdyn_PssCreateV1, però es poden crear diverses entitats en una sola acció. |
| msdyn_PssUpdateV1 | Aquesta API s'utilitza per actualitzar una entitat. L'entitat pot ser qualsevol de les entitats de planificació de projectes que admeten l'operació d'actualització. |
| msdyn_PssUpdateV2 | Aquesta API s'utilitza per actualitzar entitats. Funciona com msdyn_PssUpdateV1, però es poden actualitzar diverses entitats en una sola acció. |
| msdyn_PssDeleteV1 | Aquesta API s'utilitza per suprimir una entitat. L'entitat pot ser qualsevol de les entitats de planificació de projectes que admeten l'operació de supressió. |
| msdyn_PssDeleteV2 | Aquesta API s'utilitza per suprimir entitats. Funciona com msdyn_PssDeleteV1, però es poden suprimir diverses entitats en una sola acció. |
| msdyn_ExecuteOperationSetV1 | Aquesta API s'utilitza per executar totes les operacions dins del conjunt d'operacions determinat. |
| msdyn_PssUpdateResourceAssignmentV1 | Aquesta API s'utilitza per actualitzar un contorn de treball planificat d'una assignació de recursos. |
Ús de les API de planificació del projecte amb OperationSet
Com que els registres es creen immediatament tant per a CreateProjectV1 com per a CreateTeamMemberV1 , aquestes API no es poden utilitzar directament a l'OperationSet . Tanmateix, podeu utilitzar-los per crear els registres necessaris, crear un OperationSet i, a continuació, utilitzar els registres creats prèviament a l'OperationSet .
Operacions admeses
| Entitat de planificació | Creació | Actualitza | Suprimeix | Consideracions importants |
|---|---|---|---|---|
| Tasca del projecte | Sí | Sí | Sí | Els camps EffortCompleted i EffortRemaining es poden editar al Project per al web, però no es poden editar al Project Operations. |
| Dependència de les tasques del projecte | Sí | No | Sí | Els registres de dependència de les tasques del projecte no s'actualitzen. En lloc d'això, es pot suprimir un registre antic i crear-ne un de nou. |
| Assignació de recursos | Sí | Sí* | Sí | No s'admeten les operacions amb els camps següents: BookableResourceID,Effort,EffortCompleted,EffortRemaining i PlannedWork. |
| Dipòsit de projecte | Sí | Sí | Sí | El cub per defecte es crea mitjançant l'API CreateProjectV1 . La compatibilitat per crear i suprimir dipòsits de projectes s'ha afegit a la versió 16 d'actualització. |
| Membre de l'equip del projecte | Sí | Sí | Sí | Per a l'operació de creació, utilitzeu l'API CreateTeamMemberV1 . |
| Projecte | Sí | Sí | No | No s'admeten les operacions amb els camps següents: StateCode,BulkGenerationStatus,GlobalRevisionToken,CalendarID,Effort,EffortCompleted,EffortRemaining,Progress,Finish,TaskEarliestStart i Duration. |
| Llistes de comprovació del projecte | Sí | Sí | Sí | |
| Etiqueta del projecte | No | Sí | No | Els noms de les etiquetes es poden canviar. Aquesta funció només està disponible per a Project for the Web. Les etiquetes es creen el primer cop s'obre un projecte. |
| Tasca de projecte que s'ha d'etiqueta | Sí | No | Sí | Aquesta funció només està disponible per a Project for the Web. |
| Esprint del projecte | Sí | Sí | Sí | El camp Inici ha de tenir una data anterior al camp Finalització . Els sprints d'un mateix projecte no es poden superposar entre si. Aquesta funció només està disponible per a Project for the Web. |
| Objectiu del projecte | Sí | Sí | Sí | No s'admeten les operacions amb els camps següents: DescriptionPlainText, TaskDisplayOrder |
| Tasca de projecte per als objectius | Sí | No | Sí | No s'admeten les operacions amb els camps següents: TaskDisplayOrder |
* Els registres d'assignació de recursos no s'actualitzen. En lloc d'això, es pot suprimir el registre antic i crear-ne un de nou. Es proporciona una API independent per actualitzar els contorns d'assignació de recursos.
La propietat ID és opcional. Si es proporciona la propietat ID, el sistema intenta utilitzar-la i llança una excepció si no es pot utilitzar. Si no es proporciona, el sistema el genera.
Limitacions i problemes coneguts
La llista següent mostra les limitacions i els problemes coneguts:
Les API de planificació de projectes només poden ser utilitzades per usuaris amb llicència de projecte Microsoft. No les poden utilitzar:
- Usuaris de l'aplicació
- Usuaris del sistema
- Usuaris d'integració
- Altres usuaris que no tenen la llicència necessària
Cada OperationSet només pot tenir un màxim de 200 operacions.
Cada usuari només pot tenir un màxim de 10 OperationSets oberts .
Cada operació Actualitza el contorn d'assignació de recursos es compta com una única operació.
Cada llista de contorns actualitzats pot contenir un màxim de 100 porcions de temps.
L'estat d'error i els registres d'errors d'OperationSet no estan disponibles actualment.
Hi ha un màxim de 400 sprints per projecte.
Gestió d'errors
- Per revisar els errors generats a partir dels conjunts d'operacions, aneu a Configuració>Programar conjunts> d'operacions d'integració.
- Per revisar els errors generats des del servei de planificació del projecte, aneu a Registres> d'errors de PSS d'integració>de la planificació de la configuració.
Edició dels contorns d'assignació de recursos
A diferència de la resta d'API de planificació de projectes que actualitzen una entitat, l'API de contorn d'assignació de recursos només és responsable de les actualitzacions d'un sol camp, msdyn_plannedwork, en una única entitat, msydn_resourceassignment.
El mode de planificació proporcionat és:
- unitats fixes.
- El calendari del projecte és de 9:00 a 5:00 PM (hora del Pacífic) dilluns, dimarts, dijous i divendres. (No hi ha feina els dimecres.)
- El calendari de recursos és de 9:00 a 13:00 (hora del Pacífic) de dilluns a divendres.
Aquesta tasca és d'una setmana, quatre hores al dia perquè el calendari de recursos és de 9:00 a 13:00 (hora del Pacífic), o quatre hores al dia.
| Tasca | Data d’inici | Data d’acabament | Quantitat | 13/6/2022 | 14/6/2022 | 15/6/2022 | 16/6/2022 | 17/6/2022 | |
|---|---|---|---|---|---|---|---|---|---|
| 9-1 treballador | T1 | 13/6/2022 | 17/6/2022 | 20 | 4 | 4 | 4 | 4 | 4 |
Per exemple, si voleu que el treballador només treballi tres hores cada dia aquesta setmana i tingui una hora per a altres tasques.
Càrrega útil d'exemple d'UpdatedContours
[{
"minutes":900.0,
"start":"2022-06-13T00:00:00-07:00",
"end":"2022-06-18T00:00:00-07:00"
}]
Aquesta és l'assignació després d'executar l'API de planificació de contorn d'actualització.
| Tasca | Data d’inici | Data d’acabament | Quantitat | 13/6/2022 | 14/6/2022 | 15/6/2022 | 16/6/2022 | 17/6/2022 | |
|---|---|---|---|---|---|---|---|---|---|
| 9-1 treballador | T1 | 13/6/2022 | 17/6/2022 | 15 | 3 | 3 | 3 | 3 | 3 |
Escenari d'exemple
En aquest escenari, creeu un projecte, un membre de l'equip, quatre tasques i dues assignacions de recursos. A continuació, actualitzeu una tasca, actualitzeu el projecte, actualitzeu un contorn d'assignació de recursos, suprimiu una tasca, suprimiu una assignació de recursos i creeu una dependència de tasca.
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....");
Mostres addicionals
#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