Използвайте API за график на проекти, за да извършвате операции с обекти за планиране
Прилага се за: Project Operations за сценарии, базирани на ресурси/без запас, Lite внедряване - сделка за проформа фактуриране.
Структури за планиране
API за планиране на проекти предоставят възможност за извършване на операции за създаване, актуализиране и изтриване с обекти за планиране. Тези обекти се управляват чрез механизма за планиране в Project for the web. Операциите за създаване, актуализиране и изтриване с обекти за планиране бяха ограничени в по-ранните Dynamics 365 Project Operations версии.
Следващата таблица предоставя пълен списък на обектите от графика на проекта.
Име на обекта | Логическо име на обект |
---|---|
Project | msdyn_project |
Задача по проект | msdyn_projecttask |
Зависимост на задачи от проект | msdyn_projecttaskdependency |
Назначаване на ресурс | msdyn_resourceassignment |
Набор на проекта | msdyn_projectbucket |
Член на екипа на проект | msdyn_projectteam |
Контролни списъци на проекта | msdyn_projectchecklist |
Етикет на проект | msdyn_projectlabel |
Задача по проект спрямо етикет | msdyn_projecttasktolabel |
Спринт на проект | msdyn_projectsprint |
OperationSet
OperationSet е модел на единица работа, който може да се използва, когато в рамките на една транзакция трябва да бъдат обработени няколко искания, засягащи графика.
API за график на проекти
По-долу е даден списък на текущите API на график на проекта.
API | Описание |
---|---|
msdyn_CreateProjectV1 | Този API се използва за създаване на проект. Проектът и сегментът на проекта по подразбиране се създават незабавно. Създаването на проект може да се извърши и чрез добавяне на ред към таблицата на проекта с помощта на стандартни Dataverse API. Този процес не създава кофа по подразбиране за проекта, но може да има по-добра производителност. |
msdyn_CreateTeamMemberV1 | Този API се използва за създаване на член на екипа на проекта. Записът на члена на екипа се създава незабавно. Създаването на член на екипа може да се извърши и чрез добавяне на ред към таблицата "Член на екипа на проекта" с помощта на стандартни Dataverse API. |
msdyn_CreateOperationSetV1 | Този API се използва за планиране на няколко заявки, които трябва да бъдат изпълнени в рамките на транзакция. |
msdyn_PssCreateV1 | Този API се използва за създаване на обект. Обектът може да бъде всеки от обектите за планиране на проекта, които поддържат операцията за създаване. |
msdyn_PssCreateV2 | Този API се използва за създаване на обект. Работи като msdyn_PssCreateV1, но в едно действие могат да бъдат създадени множество обекти. |
msdyn_PssUpdateV1 | Този API се използва за актуализиране на обект. Обектът може да бъде всеки от обектите за планиране на проекта, които поддържат операцията за актуализиране. |
msdyn_PssUpdateV2 | Този API се използва за актуализиране на обекти. Работи като msdyn_PssUpdateV1, но множество обекти могат да бъдат актуализирани в едно действие. |
msdyn_PssDeleteV1 | Този API се използва за изтриване на обект. Обектът може да бъде всеки от обектите за планиране на проекта, които поддържат операцията за изтриване. |
msdyn_PssDeleteV2 | Този API се използва за изтриване на обекти. Работи като msdyn_PssDeleteV1, но множество обекти могат да бъдат изтрити с едно действие. |
msdyn_ExecuteOperationSetV1 | Този API се използва за изпълнение на всички операции в рамките на дадения набор от операции. |
msdyn_PssUpdateResourceAssignmentV1 | Този API се използва за актуализиране на планиран работен контур на присвояване на ресурси. |
Използване на API за график на проекти с OperationSet
Тъй като записите се създават незабавно както за CreateProjectV1 , така и за CreateTeamMemberV1, тези API не могат да се използват директно в OperationSet. Можете обаче да ги използвате, за да създадете необходимите записи, да създадете OperationSet и след това да използвате предварително създадените записи в OperationSet.
Поддържани операции
Обект за планиране | Създай | Актуализиране | Delete | Важни съображения |
---|---|---|---|---|
Задача от проект | Да | Да | Да | Полетата Progress,EffortCompleted и EffortRemaining могат да се редактират в Project for the Web, но не могат да се редактират в Project Operations. |
Зависимост на задача от проект | Да | No | Да | Записите на зависимостта на проектната задача не се актуализират. Вместо това може да се изтрие стар запис и да се създаде нов. |
Назначаване на ресурс | Да | Да* | Да | Операции със следните полета не се поддържат: BookableResourceID,Effort,EffortCompleted,EffortRemaining и Planned Work. |
Набор на проекта | Да | Да | Да | Контейнерът по подразбиране се създава с помощта на API на CreateProjectV1 . Поддръжката за създаване и изтриване на пакети за проекти беше добавена в издание на актуализацията 16. |
Член на екипа на проект | Да | Да | Да | За операцията за създаване използвайте API на CreateTeamMemberV1 . |
Project | Да | Да | No | Операции със следните полета не се поддържат: StateCode,BulkGenerationStatus,GlobalRevisionToken,CalendarID,Effort,EffortCompleted,EffortRemaining,Progress,Finish,TaskEarlyestStart и Duration. |
Контролни списъци на проекта | Да | Да | Да | |
Етикет на проект | No | Да | No | Имената на етикетите могат да се променят. Тази функция е налична само за Project for Web. Етикетите се създават още при първото отваряне на проект. |
Задача по проект спрямо етикет | Да | No | Да | Тази функция е налична само за Project for Web. |
Спринт на проект | Да | Да | Да | Полето "Старт " трябва да има дата, по-ранна от полето "Край". Спринтовете за един и същ проект не могат да се припокриват. Тази функция е налична само за Project for Web. |
Цел на проект | Да | Да | Да | Операции със следните полета не се поддържат: DescriptionPlainText, TaskDisplayOrder |
Задача по проект спрямо цел | Да | No | Да | Операции със следните полета не се поддържат: TaskDisplayOrder |
* Записите за присвояване на ресурси не се актуализират. Вместо това може да се изтрие старият запис и да се създаде нов. Предоставя се отделен API за актуализиране на контурите на присвояване на ресурси.
Свойството ИД не е задължително. Ако е предоставено свойството ID, системата се опитва да го използва и хвърля изключение, ако не може да се използва. Ако не е предоставен, системата го генерира.
Ограничения и известни проблеми
Следният списък показва ограничения и известни проблеми:
API за график на проекти могат да се използват само от потребители с лиценз за Microsoft Project. Те не могат да бъдат използвани от:
- Потребители на приложение
- Системни потребители
- Потребители на интеграция
- Други потребители, които нямат необходимия лиценз
Всеки OperationSet може да има максимум 200 операции.
Всеки потребител може да има максимум 10 отворени набора от операции.
Всяка операция за актуализиране на контура за присвояване на ресурси се брои като една операция.
Всеки списък с актуализирани контури може да съдържа максимум 100 времеви отрязъка.
Състоянието на грешката и регистрационните файлове за грешки на OperationSet в момента не са налични.
Има максимум 400 спринта на проект.
Обработка на грешки
- За да прегледате грешките, генерирани от наборите от операции, отидете на Настройки>Настройки Планиране на интеграционни>операции Набори.
- За да прегледате грешките, генерирани от услугата за планиране на проекти, отидете на Настройки Интеграция>на графика PSS Регистрационни> файлове за грешки.
Редактиране на контури за присвояване на ресурси
За разлика от всички други API за планиране на проекти, които актуализират обект, API за контур на присвояване на ресурси е единствено отговорен за актуализациите на едно поле, msdyn_plannedwork, на един обект, msydn_resourceassignment.
Даденият режим на график е:
- фиксирани единици.
- Календарът на проекта е от 9:00 до 5:00 ч. (тихоокеанско време) понеделник, вторник, четвъртък и петък. (В сряда няма работа.)
- Календарът на ресурсите е от 9:00 до 13:00 ч. (тихоокеанско време) от понеделник до петък.
Тази задача е за една седмица, четири часа на ден, тъй като календарът на ресурсите е от 9:00 до 13:00 ч. (тихоокеанско време) или четири часа на ден.
Задача | Начална дата | Крайна дата | Количество | 6/13/2022 | 6/14/2022 | 6/15/2022 | 6/16/2022 | 6/17/2022 | |
---|---|---|---|---|---|---|---|---|---|
9-1 работник | T1 | 6/13/2022 | 6/17/2022 | 20 | 4 | 4 | 4 | 4 | 4 |
Например, ако искате работникът да работи само три часа всеки ден през тази седмица и да отделите един час за други задачи.
Актуализиран полезен товар на примера на Contours
[{
"minutes":900.0,
"start":"2022-06-13T00:00:00-07:00",
"end":"2022-06-18T00:00:00-07:00"
}]
Това е присвояването след стартиране на API за актуализиране на контурен график.
Задача | Начална дата | Крайна дата | Количество | 6/13/2022 | 6/14/2022 | 6/15/2022 | 6/16/2022 | 6/17/2022 | |
---|---|---|---|---|---|---|---|---|---|
9-1 работник | T1 | 6/13/2022 | 6/17/2022 | 15 | 3 | 3 | 3 | 3 | 3 |
Примерен сценарий
В този сценарий създавате проект, член на екипа, четири задачи и две назначения на ресурси. След това актуализирате една задача, актуализирате проекта, актуализирате контура на присвояване на ресурс, изтривате една задача, изтривате едно присвояване на ресурс и създавате зависимост на задача.
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....");
Допълнителни проби
#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