استخدام واجهات برمجة تطبيقات جدولة المشاريع لتنفيذ عمليات مع كيانات الجدولة

ينطبق على: Project Operations Integrated with ERP, Project Operations Core.

جدولة الكيانات

توفر واجهات برمجة تطبيقات جدولة المشروع القدرة على إنشاء كيانات الجدولة وتحديثها وحذفها. يدير محرك الجدولة في Project للويب هذه الكيانات. الإصدارات السابقة من 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 الوصف
msdyn_CreateProjectV1 استخدم واجهة برمجة التطبيقات هذه لإنشاء مشروع. يقوم على الفور بإنشاء المشروع ومستودع المشروع الافتراضي. يمكنك أيضا إنشاء مشروع عن طريق إضافة صف إلى جدول المشروع باستخدام واجهات برمجة تطبيقات Dataverse القياسية. لا تنشئ هذه العملية مستودعا افتراضيا للمشروع ولكن قد يكون لها أداء أفضل.
msdyn_CreateTeamMemberV1 استخدم واجهة برمجة التطبيقات هذه لإنشاء عضو فريق مشروع. يقوم على الفور بإنشاء سجل عضو الفريق. يمكنك أيضا إنشاء عضو فريق عن طريق إضافة صف إلى جدول عضو فريق المشروع باستخدام واجهات برمجة تطبيقات Dataverse القياسية.
msdyn_CreateOperationSetV1 استخدم واجهة برمجة التطبيقات هذه لجدولة العديد من الطلبات التي يجب تنفيذها ضمن معاملة.
msdyn_PssCreateV1 استخدم واجهة برمجة التطبيقات هذه لإنشاء كيان. يمكن أن يكون الكيان أيًا من كيانات جدولة المشروع التي تدعم عملية الإنشاء.
msdyn_PssCreateV2 استخدم واجهة برمجة التطبيقات هذه لإنشاء كيان. يعمل مثل msdyn_PssCreateV1، ولكن يمكنك إنشاء كيانات متعددة في إجراء واحد.
msdyn_PssUpdateV1 استخدم واجهة برمجة التطبيقات هذه لتحديث كيان. يمكن أن يكون الكيان أيًا من كيانات جدولة المشروع التي تدعم عملية التحديث.
msdyn_PssUpdateV2 استخدم واجهة برمجة التطبيقات هذه لتحديث الكيانات. يعمل مثل msdyn_PssUpdateV1، ولكن يمكنك تحديث كيانات متعددة في إجراء واحد.
msdyn_PssDeleteV1 استخدم واجهة برمجة التطبيقات هذه لحذف كيان. يمكن أن يكون الكيان أيًا من كيانات جدولة المشروع التي تدعم عملية الحذف.
msdyn_PssDeleteV2 استخدم واجهة برمجة التطبيقات هذه لحذف الكيانات. يعمل مثل msdyn_PssDeleteV1، ولكن يمكنك حذف كيانات متعددة في إجراء واحد.
msdyn_ExecuteOperationSetV1 استخدم واجهة برمجة التطبيقات هذه لتنفيذ جميع العمليات داخل مجموعة العمليات المحددة.
msdyn_PssUpdateResourceAssignmentV1 استخدم واجهة برمجة التطبيقات هذه لتحديث مخطط عمل مخطط تعيين المورد.

استخدام واجهات برمجة التطبيقات لجدولة المشروع مع OperationSet

نظرا لأن كل من CreateProjectV1وCreateTeamMemberV1 ينشئان سجلات على الفور، فلا يمكنك استخدام واجهات برمجة التطبيقات هذه مباشرة في OperationSet. ومع ذلك، يمكنك استخدام واجهات برمجة التطبيقات هذه لإنشاء السجلات المطلوبة، وإنشاء OperationSet، ثم استخدام السجلات التي تم إنشاؤها مسبقا في OperationSet.

العمليات المدعومة

كيان الجدولة Create Update Delete اعتبارات مهمة
مهمة المشروع نعم نعم نعم يمكنك تحرير الحقلين EffortCompleted و EffortRemaining في Project للويب، ولكن لا يمكنك تحرير هذه الحقول في Project Operations.
تبعية مهمة المشروع نعم لا نعم لا يتم تحديث سجلات تبعية مهمة المشروع. بدلا من ذلك، يمكنك حذف سجل قديم وإنشاء سجل جديد.
تعيين المورد نعم ‏‏نعم‬* نعم العمليات مع الحقول التالية غير مدعومة: BookableResourceID وEffort وEffortCompleted وEffortRemaining ةPlannedWork.
مستودع المشروع نعم نعم نعم يتم إنشاء الحاوية الافتراضية باستخدام واجهة برمجة تطبيقات CreateProjectV1. تمت إضافة دعم إنشاء حاويات المشروع وحذفها في تحديث الإصدار 16.
عضو فريق المشروع نعم نعم نعم بالنسبة لعملية الإنشاء، استخدم واجهة برمجة التطبيقات CreateTeamMemberV1.
Project نعم نعم لا العمليات مع الحقول التالية غير مدعومة: StateCode وBulkGenerationStatus وGlobalRevisionToken وCalendarID وEffort وEffortCompleted وEffortRemaining وProgress وFinish وTaskEarliestStart وDuration.
قوائم اختيار المشروع نعم نعم نعم
تسمية المشروع لا نعم لا يمكنك تغيير أسماء التسميات. هذه الميزة متاحة فقط لـ Project for the Web. يتم إنشاء التسميات في المرة الأولى التي تفتح فيها مشروعا.
مهمة المشروع المُراد تسميتها نعم لا نعم هذه الميزة متاحة فقط لـ Project for the Web.
الدورة المتكررة للمشروع‬ نعم نعم نعم يجب أن يكون حقل البدء أقدم من حقل الإنهاء. لا يمكن أن تتداخل الدورات المتكررة لنفس المشروع مع بعضها البعض. هذه الميزة متاحة فقط لـ Project for the Web.
هدف المشروع نعم نعم نعم العمليات مع الحقول التالية غير مدعومة: DescriptionPlainText وTaskDisplayOrder
مهمة المشروع إلى الهدف نعم لا نعم العمليات مع الحقول التالية غير مدعومة: TaskDisplayOrder

* لا يتم تحديث سجلات تعيين الموارد. بدلا من ذلك، يمكنك حذف السجل القديم وإنشاء سجل جديد. يتم توفير واجهة برمجة تطبيقات منفصلة لتحديث حدود تعيين الموارد.

خاصية المعرف اختيارية. إذا قمت بتوفير خاصية المعرف، يحاول النظام استخدامها ويطرح استثناء إذا تعذر استخدامها. إذا لم توفر خاصية المعرف، فسينشئها النظام.

القيود والمشاكل المعروفة

تعرض القائمة التالية القيود والمشاكل المعروفة:

  • يمكن فقط للمستخدمين الذين لديهم ترخيص Microsoft Project استخدام واجهات برمجة تطبيقات جدولة المشروع. لا يمكن للمستخدمين التاليين استخدام واجهات برمجة التطبيقات هذه:

    • مستخدمو التطبيق
    • مستخدمو النظام
    • مستخدمو التكامل
    • المستخدمين الآخرين الذين ليس لديهم الترخيص المطلوب
  • يمكن أن يكون لكل OperationSet ما يصل إلى 200 عملية فقط.

  • يمكن أن يكون لكل مستخدم ما يصل إلى 10 مجموعات عمليات مفتوحة فقط.

  • يتم احتساب كل عملية تحديث تعيين المورد كعملية واحدة.

  • يمكن أن تحتوي كل قائمة من خطوط الكونتور المحدثة على ما يصل إلى 100 فاصل زمني.

  • في الوقت الحالي، لا تتوفر حالة فشل OperationSet وسجلات الفشل.

  • يمكن أن يكون لكل مشروع ما يصل إلى 400 دورة متكررة.

  • الحدود والقيود الموضوعة على المشاريع والمهام‬‏‫

معالجة الخطأ

  • لمراجعة الأخطاء الناتجة عن مجموعات العمليات، انتقل إلىالاعدادات>تكامل الجدول الزمني>مجموعات العمليات.
  • لمراجعة الأخطاء الناتجة عن خدمة جدول المشروع، انتقل إلى الاعدادات>تكامل الجدول الزمني>سجلات أخطاء PSS.

يتم الآن تحرير مخططات تعيين الموارد

على عكس جميع واجهات API الْأخرى لجدولة الْمشروع الْتي تقوم بتحديث الْكيان، إن واجهة API تعيين الْموارد هي الْمسؤولة وحدها عن الْتحديثات لحقل واحد، msdyn_plannedwork، في كيان واحد، msydn_resourceassignment.

وضع الجدولة المحدد هو:

  • وحدات ثابتة
  • يتراوح تقويم المشروع من الساعة 9:00 صباحا إلى 5:00 مساء (توقيت المحيط الهادئ) من الاثنين والثلاثاء والخميس والجمعة. (لا يوجد عمل أيام الأربعاء.)
  • تقويم المورد من 9:00 إلى 1:00 مساء (توقيت الباسيفيكي) من الإثنين إلى الجمعة.

مدة هذا التعيين هي أسبوع واحد، أربع ساعات يوميًا لأن تقويم الموارد يبدأ من 9:00 صباحًا إلى 1:00 مساءً (بتوقيت المحيط الهادئ)، أو أربع ساعات يوميًا.

  المهمة تاريخ البدء تاريخ الانتهاء Quantity 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

على سبيل المثال، إذا كنت تريد أن يعمل العامل ثلاث ساعات فقط كل يوم في هذا الأسبوع ويسمح له بساعة واحدة للقيام بمهام أخرى.

حمولة عينة UpdatedContours:

[{

"minutes":900.0,

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

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

}]

يتم هذا التعيين بعد تشغيل Update Contour Schedule API.

  المهمة تاريخ البدء تاريخ الانتهاء Quantity 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