Споделяне чрез


Използвайте 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