Sử dụng các API lịch trình dự án để thực hiện các hoạt động với các thực thể Lập lịch biểu

Áp dụng cho: Project Operations cho các kịch bản dựa trên tài nguyên/không tồn kho, Triển khai Lite - lập hóa đơn chiếu lệ.

Thực thể lập lịch trình

API lịch trình dự án cung cấp khả năng thực hiện các thao tác tạo, cập nhật và xóa với các thực thể Lập lịch. Các thực thể này được quản lý thông qua công cụ Lập lịch trình trong Dự án cho web. Các thao tác tạo, cập nhật và xóa với các thực thể Lập lịch biểu đã bị hạn chế trong các bản phát hành trước đó Dynamics 365 Project Operations .

Bảng sau cung cấp danh sách đầy đủ các thực thể lịch trình của Dự án.

Tên thực thể Tên logic của thực thể
Dự án msdyn_project
Nhiệm vụ dự án msdyn_projecttask
Quan hệ phụ thuộc Nhiệm vụ Dự án msdyn_projecttaskdependency
Gán Nguồn lực msdyn_resourceassignment
Bộ chứa Dự án msdyn_projectbucket
Thành viên Nhóm Dự án msdyn_projectteam
Danh sách kiểm tra dự án msdyn_projectchecklist
Nhãn dự án msdyn_projectlabel
Nhiệm vụ dự án để gắn nhãn msdyn_projecttasktolabel
Phân đoạn nước rút trong dự án msdyn_projectsprint

Bộ hoạt động

OperationSet là một mẫu đơn vị công việc có thể được sử dụng khi phải xử lý một số yêu cầu tác động lên lịch trình trong một giao dịch.

API tiến độ dự án

Sau đây là danh sách các API lịch trình Dự án hiện tại.

API Description
msdyn_CreateProjectV1 API này được sử dụng để tạo dự án. Nhóm dự án và dự án mặc định được tạo ngay lập tức. Việc tạo dự án cũng có thể được thực hiện bằng cách thêm một hàng vào bảng dự án bằng API tiêu chuẩn Dataverse . Quá trình này sẽ không tạo vùng lưu trữ mặc định cho dự án nhưng có thể có hiệu suất tốt hơn.
msdyn_CreateTeamMemberV1 API này được sử dụng để tạo thành viên nhóm dự án. Hồ sơ thành viên trong nhóm được tạo ngay lập tức. Việc tạo Thành viên nhóm cũng có thể được thực hiện bằng cách thêm một hàng vào bảng Thành viên nhóm dự án bằng API tiêu chuẩn Dataverse .
msdyn_CreateOperationSetV1 API này được dùng để lập lịch trình một số yêu cầu phải được thực hiện trong một giao dịch.
msdyn_PssCreateV1 API này được sử dụng để tạo thực thể. Thực thể có thể là bất kỳ thực thể lập lịch trình Dự án nào hỗ trợ hoạt động tạo.
msdyn_PssCreateV2 API này được sử dụng để tạo thực thể. Nó hoạt động như msdyn_PssCreateV1, nhưng nhiều thực thể có thể được tạo trong một hành động.
msdyn_PssUpdateV1 API này được sử dụng để cập nhật thực thể. Thực thể có thể là bất kỳ thực thể lập lịch trình Dự án nào hỗ trợ hoạt động cập nhật.
msdyn_PssUpdateV2 API này được sử dụng cho các thực thể được cập nhật. Nó hoạt động như msdyn_PssUpdateV1, nhưng nhiều thực thể có thể được cập nhật trong một hành động.
msdyn_PssDeleteV1 API này được sử dụng để xóa thực thể. Thực thể có thể là bất kỳ thực thể lập lịch trình Dự án nào hỗ trợ hoạt động xóa.
msdyn_PssDeleteV2 API này được sử dụng để xóa các thực thể. Nó hoạt động như msdyn_PssDeleteV1, nhưng nhiều thực thể có thể bị xóa trong một hành động.
msdyn_ExecuteOperationSetV1 API này được dùng để thực thi tất cả các thao tác trong nhóm thao tác nhất định.
msdyn_PssUpdateResourceAssignmentV1 API này được sử dụng để cập nhật đường viền công việc đã lên kế hoạch Phân công nguồn lực.

Sử dụng API lịch trình dự án với OperationSet

Vì các bản ghi được tạo ngay lập tức cho cả CreateProjectV1CreateTeamMemberV1, các API này không thể được sử dụng trực tiếp trong OperationSet. Tuy nhiên, bạn có thể sử dụng chúng để tạo các bản ghi cần thiết, tạo một OperationSet, và sau đó sử dụng các bản ghi được tạo sẵn trong OperationSet.

Hoạt động được hỗ trợ

Thực thể lập lịch trình Tạo Cập nhật Xoá Những điều quan trọng cần cân nhắc
Nhiệm vụ dự án Có Có Có Các trường Tiến độ,EffortCompletedEffortRemaining có thể được chỉnh sửa trong Project cho Web, nhưng không thể chỉnh sửa chúng trong Project Operations.
Quan hệ phụ thuộc nhiệm vụ dự án Có No Có Bản ghi quan hệ phụ thuộc nhiệm vụ dự án không được cập nhật. Thay vào đó, một bản ghi cũ có thể bị xóa và một bản ghi mới có thể được tạo.
Công việc giao cho nguồn lực Có Có* Có Các thao tác với các trường sau không được hỗ trợ: BookableResourceID, Effort, EffortComplete, EffortRemainingPlannedWork.
Nhóm dự án Có Có Có Bộ chứa mặc định được tạo bằng cách sử dụng API CreateProjectV1 . Hỗ trợ tạo và xóa bộ chứa dự án đã được thêm vào trong Bản phát hành cập nhật 16.
Thành viên nhóm dự án Có Có Có Đối với thao tác tạo, hãy sử dụng API CreateTeamMemberV1 .
Dự án Có Có Các thao tác với các trường sau không được hỗ trợ: StateCode, BulkGenerationStatus, GlobalRevisionToken , ID lịch, Nỗ lực, Nỗ lực đã hoàn thành, Nỗ lực còn lại, Tiến bộ, Kết thúc, TaskEarliestStartThời lượng.
Danh sách kiểm tra dự án Có Có Có
Nhãn dự án No Có No Tên nhãn có thể được thay đổi. Tính năng này chỉ sẵn dùng cho Project cho Web. Nhãn được tạo khi bạn mở dự án lần đầu tiên.
Nhiệm vụ dự án để gắn nhãn Có No Có Tính năng này chỉ sẵn dùng cho Project cho Web.
Phân đoạn nước rút trong dự án Có Có Có Trường Bắt đầu phải có ngày sớm hơn trường Kết thúc . Các lần chạy nước rút cho cùng một dự án không được chồng chéo lên nhau. Tính năng này chỉ sẵn dùng cho Project cho Web.
Mục tiêu dự án Có Có Có Các thao tác với các trường sau không được hỗ trợ: DescriptionPlainText, TaskDisplayOrder
Nhiệm vụ dự án dẫn đến mục tiêu Có No Có Hoạt động với các trường sau không được hỗ trợ: TaskDisplayOrder

* Hồ sơ phân công tài nguyên không được cập nhật. Thay vào đó, một bản ghi cũ có thể bị xóa và một bản ghi mới có thể được tạo. Một API riêng biệt đã được cung cấp để cập nhật các đường viền Phân công nguồn lực.

Thuộc tính ID là không bắt buộc. Nếu nó được cung cấp, hệ thống sẽ cố gắng sử dụng nó và đưa ra một ngoại lệ nếu không thể sử dụng được. Nếu nó không được cung cấp, hệ thống sẽ tạo ra nó.

Hạn chế và các vấn đề đã biết

Sau đây là danh sách các giới hạn và vấn đề đã biết:

  • API lịch trình dự án chỉ có thể được sử dụng bởi Người dùng có Giấy phép dự án Microsoft. Những đối tượng sau không thể sử dụng API lịch trình:

    • Người dùng ứng dụng
    • Người dùng hệ thống
    • Người dùng tích hợp
    • Những người dùng khác không có giấy phép bắt buộc
  • Mỗi OperationSet chỉ có thể có tối đa 200 thao tác.

  • Mỗi người dùng chỉ có thể có tối đa 10 bộ hoạt động mở .

  • Project Operations hiện hỗ trợ tổng cộng tối đa 1000 nhiệm vụ trên một dự án.

  • Mỗi thao tác Cập nhật đường viền phân công nguồn lực tính là một thao tác đơn lẻ.

  • Mỗi danh sách các đường viền được cập nhật có thể chứa tối đa 100 lát cắt thời gian.

  • OperationSet trạng thái lỗi và nhật ký lỗi hiện không có sẵn.

  • Có tối đa 400 lần chạy nước rút cho mỗi dự án.

  • Những giới hạn, ranh giới đối với các dự án, nhiệm vụ.

  • Các nhãn hiện chỉ có sẵn trong Project for the Web.

  • Nhãn được tạo vào lần đầu tiên bạn mở một dự án.

  • Có tối đa 10 mục tiêu cho mỗi dự án.

  • Mỗi nhiệm vụ có thể xuất hiện trong Project Task to Goal một lần.

Xử lý lỗi

  • Để xem lại các lỗi được tạo từ Bộ thao tác, hãy đi tới Cài đặt>Tích hợp lịch biểu>Bộ thao tác.
  • Để xem lại các lỗi phát sinh từ Dịch vụ lập lịch dự án, hãy chuyển đến Cài đặt>Tích hợp lịch trình>Nhật ký lỗi PSS.

Chỉnh sửa đường viền gán tài nguyên

Không giống như tất cả các API lập lịch dự án khác có thể cập nhật một thực thể, API đường viên phân công tài nguyên chỉ chịu trách nhiệm cập nhật cho một trường duy nhất, msdyn_plannedwork, trên một thực thể duy nhất, msydn_resourceassignment.

Chế độ lịch trình đã cho là:

  • đơn vị cố định.
  • Lịch dự án là từ 9:00 đến 5:00 chiều (Giờ Thái Bình Dương) Thứ Hai, Thứ Ba, Thứ Năm và Thứ Sáu. (Không có việc làm vào thứ Tư.)
  • Lịch tài nguyên là từ 9:00 sáng đến 1:00 chiều (Giờ Thái Bình Dương) từ Thứ Hai đến Thứ Sáu.

Phân công này là cho một tuần, bốn giờ một ngày. Điều này là do lịch tài nguyên là từ 9 giờ sáng đến 1 giờ chiều (Giờ Thái Bình Dương) hoặc bốn giờ một ngày.

  Tác vụ Ngày bắt đầu Ngày kết thúc Số lượng 13/6/2022 14/6/2022 15/6/2022 16/6/2022 17/6/2022
nhân viên 9-1 T1 13/6/2022 17/6/2022 20 Tệp 4 Tệp 4 Tệp 4 Tệp 4 Tệp 4

Ví dụ: nếu bạn muốn nhân viên chỉ làm việc ba giờ mỗi ngày trong tuần này và cho phép một giờ cho các nhiệm vụ khác.

Đã cập nhật tải trọng mẫu Đường viền

[{

"minutes":900.0,

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

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

}]

Đây là nhiệm vụ sau khi chạy API lình trình cập nhật đường viền.

  Tác vụ Ngày bắt đầu Ngày kết thúc Số lượng 13/6/2022 14/6/2022 15/6/2022 16/6/2022 17/6/2022
nhân viên 9-1 T1 13/6/2022 17/6/2022 15 3 3 3 3 3

Kịch bản mẫu

Trong kịch bản này, bạn tạo một dự án, một thành viên nhóm, bốn nhiệm vụ và hai nhiệm vụ tài nguyên. Tiếp theo, bạn cập nhật một nhiệm vụ, cập nhật dự án, cập nhật đường viền phân công tài nguyên, xóa một nhiệm vụ, xóa một nhiệm vụ tài nguyên và tạo một phần phụ thuộc nhiệm vụ.

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....");

Mẫu bổ sung

#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