Zamanlama varlıklarıyla işlem gerçekleştirmek için Proje zamanlama API'lerini kullanma

Uygulandığı Alanlar: ERP ile Entegre Proje Operasyonları, Proje Operasyonları Çekirdeği.

Zamanlama varlıkları

Proje zamanlama API'leri Zamanlama varlıkları oluşturma, güncelleştirme ve silme olanağı sağlar. Web için Project'teki Zamanlama altyapısı bu varlıkları yönetir. Önceki Dynamics 365 Project Operations sürümleri, Scheduling varlıkları için oluşturma, güncelleme ve silme işlemlerini kısıtlamıştır.

Aşağıdaki tabloda Proje zamanlama varlıklarının tam listesi verilmiştir.

Varlık adı Varlık mantıksal adı
Project msdyn_project
Proje Görevi msdyn_projecttask
Proje Görevi Bağımlılığı msdyn_projecttaskdependency
Kaynak Atama msdyn_resourceassignment
Proje Demeti msdyn_projectbucket
Proje Takımı Üyesi msdyn_projectteam
Proje Denetim Listeleri msdyn_projekontrol_listesi
Proje Etiketi msdyn_projectlabel
Etiketlenecek Proje Görevi msdyn_projecttasktolabel
Proje Sprint'i msdyn_projectsprint

OperationSet

Bir işlem içinde zamanlamayı etkileyen birkaç isteği işlemeniz gerektiğinde OperationSet'i iş birimi olarak kullanın.

Proje zamanlama API'leri

Aşağıdaki listede geçerli Project zamanlama API'leri gösterilmektedir.

API Açıklama
msdyn_CreateProjectV1 Proje oluşturmak için bu API'yi kullanın. Projeyi ve varsayılan proje demetini hemen oluşturur. Ayrıca, standart Dataverse API'lerini kullanarak proje tablosuna satır ekleyerek de proje oluşturabilirsiniz. Bu işlem proje için varsayılan demet oluşturmaz, ancak daha iyi performansa sahip olabilir.
msdyn_CreateTeamMemberV1 Proje ekip üyesi oluşturmak için bu API'yi kullanın. Ekip üyesi kaydını hemen oluşturur. Ayrıca, standart Dataverse API'lerini kullanarak Proje Ekip Üyesi tablosuna satır ekleyerek de bir ekip üyesi oluşturabilirsiniz.
msdyn_CreateOperationSetV1 Bir işlem içinde gerçekleştirilmesi gereken birkaç istek zamanlamak için bu API'yi kullanın.
msdyn_PssCreateV1 Varlık oluşturmak için bu API'yi kullanın. Varlık, oluşturma işlemini destekleyen Proje zamanlama varlıklarından herhangi biri olabilir.
msdyn_PssCreateV2 Varlık oluşturmak için bu API'yi kullanın. msdyn_PssCreateV1 gibi çalışır, ancak tek bir eylemde birden çok varlık oluşturabilirsiniz.
msdyn_PssUpdateV1 Bir varlığı güncelleştirmek için bu API'yi kullanın. Varlık, güncelleştirme işlemini destekleyen Proje zamanlama varlıklarından herhangi biri olabilir.
msdyn_PssUpdateV2 Varlıkları güncelleştirmek için bu API'yi kullanın. msdyn_PssUpdateV1 gibi çalışır, ancak tek bir eylemde birden çok varlığı güncelleştirebilirsiniz.
msdyn_PssDeleteV1 Bir varlığı silmek için bu API'yi kullanın. Varlık, silme işlemini destekleyen Proje zamanlama varlıklarından herhangi biri olabilir.
msdyn_PssDeleteV2 Varlıkları silmek için bu API'yi kullanın. msdyn_PssDeleteV1 gibi çalışır, ancak tek bir eylemde birden çok varlığı silebilirsiniz.
msdyn_ExecuteOperationSetV1 Verilen işlem kümesindeki tüm işlemleri yürütmek için bu API'yi kullanın.
msdyn_PssUpdateResourceAssignmentV1 Kaynak Ataması planlı çalışma dağılımını güncelleştirmek için bu API'yi kullanın.

Project zamanlama API'lerini OperationSet ile kullanma

Hem CreateProjectV1 hem de CreateTeamMemberV1 hemen kayıt oluşturduğundan, bu API'leri doğrudan OperationSet'te kullanamazsınız. Ancak, gerekli kayıtları oluşturmak, bir OperationSet oluşturmak ve ardından OperationSet'te önceden oluşturulmuş kayıtları kullanmak için bu API'leri kullanabilirsiniz.

Desteklenen işlemler

Zamanlama varlığı Oluştur Güncelleştirme Sil Dikkat edilmesi gereken önemli hususlar
Proje görevi Yes Yes Yes Web için Project'te EffortCompleted ve EffortRemaining alanlarını düzenleyebilirsiniz, ancak bu alanları Project İşlemleri'nde düzenleyemezsiniz.
Proje görevi bağımlılığı Yes No Yes Proje görev bağımlılığı kayıtları güncelleştirilmemiş. Bunun yerine, eski bir kaydı silebilir ve yeni bir kayıt oluşturabilirsiniz.
Kaynak ataması Yes Evet* Yes Şu alanlara sahip işlemler desteklenmez: BookableResourceID, Effort, EffortCompleted, EffortRemaining ve PlannedWork.
Proje demeti Yes Yes Yes Varsayılan paket, CreateProjectV1 API'sini kullanarak oluşturulur. Proje paketleri oluşturma ve silme desteği, Güncelleştirme Sürümü 16'da eklenmiştir.
Proje takımı üyesi Yes Yes Yes Oluşturma işlemi için CreateTeamMemberV1 API'sini kullanın.
Project Yes Yes No Şu alanlara sahip işlemler desteklenmez: StateCode, BulkGenerationStatus, GlobalRevisionToken, CalendarID, Effort, EffortCompleted, EffortRemaining, Progress, Finish, TaskEarliestStart ve Duration.
Proje Denetim Listeleri Yes Yes Yes
Proje Etiketi No Yes No Etiket adlarını değiştirebilirsiniz. Bu özellik, yalnızca Project for the Web ile kullanılabilir. Bir projeyi ilk kez açtığınızda etiketler oluşturulur.
Etiketlenecek Proje Görevi Yes No Yes Bu özellik, yalnızca Project for the Web ile kullanılabilir.
Proje Sprint'i Yes Yes Yes Başlangıç alanındaki tarih, Bitiş alanındaki tarihten daha erken bir zaman olmalıdır. Aynı projenin sprint'leri birbiriyle çakışamaz. Bu özellik, yalnızca Project for the Web ile kullanılabilir.
Proje Hedefi Yes Yes Yes Aşağıdaki alanlara sahip işlemler desteklenmez: DescriptionPlainText, TaskDisplayOrder
Hedef için Proje Görevi Yes No Yes Aşağıdaki alanlara sahip işlemler desteklenmez: TaskDisplayOrder

* Kaynak atama kayıtları güncelleştirilmemiş. Bunun yerine, eski kaydı silebilir ve yeni bir kayıt oluşturabilirsiniz. Kaynak Atama dağılımlarını güncelleştirmek için ayrı bir API sağlanır.

Kimlik özelliği isteğe bağlıdır. Id özelliğini sağlarsanız, sistem bunu kullanmaya çalışır ve kullanılamıyorsa bir özel durum oluşturur. ID özelliğini sağlamazsanız sistem bunu oluşturur.

Sınırlamalar ve bilinen sorunlar

Aşağıdaki listede sınırlamalar ve bilinen sorunlar gösterilmektedir:

  • Proje Zamanlaması API'lerini yalnızca Microsoft Project Lisansı olan kullanıcılar kullanabilir. Aşağıdaki kullanıcılar bu API'leri kullanamaz:

    • Uygulama kullanıcıları
    • Sistem kullanıcıları
    • Tümleştirme kullanıcıları
    • Gerekli lisansa sahip olmayan diğer kullanıcılar
  • Her OperationSet'in en fazla 200 işlemi olabilir.

  • Her kullanıcının en fazla 10 açık OperationSet'i olabilir.

  • Her Kaynak Atama dağılımını güncelleştirme işlemi tek bir işlem olarak sayılır.

  • Güncelleştirilmiş dağılımların her listesi en fazla 100 zaman dilimi içerebilir.

  • OperationSet hata durumu ve hata günlükleri şu anda kullanılamıyor.

  • Her projenin en fazla 400 sprint'i olabilir.

  • Proje ve görevlerde sınır ve kısıtlamalar.

Hata işleme

  • İşlem kümelerinden oluşturulan hataları gözden geçirmek için Ayarlar>Zamanlama tümleştirme>İşlem kümeleri'ne gidin.
  • Proje zamanlama hizmetinden oluşturulan hataları gözden geçirmek için Ayarlar>Zamanlama tümleştirmesi>PSS Hata günlükleri'ne gidin.

Kaynak atama sınırlarını düzenleme

Bir varlığı güncelleştiren diğer tüm proje zamanlama API'lerinin aksine, kaynak atama dağılımı API'si yalnızca tek bir varlık msdyn_plannedworküzerinde tek bir alanı msydn_resourceassignmentgüncelleştirir.

Zamanlama modunun aşağıdaki gibi olduğunu varsayalım:

  • sabit birimler.
  • Proje takvimi Pazartesi, Salı, Perşembe ve Cuma günleri 09:00 ile 17:00 (Pasifik Saati) arasındadır. (Çarşamba günleri iş yok.)
  • Kaynak takvimi Pazartesi ila Cuma günleri 09.00 - 13.00 (Pasifik Saati) arasındadır.

Kaynak takvimi 09:00 - 13:00 (Pasifik Saati) arasında veya günde dört saat olduğundan bu atama bir haftalık, günde dört saattir.

  Görev Başlangıç Tarihi Bitiş Tarihi Miktar 13/6/2022 14/6/2022 15/06/2022 16/6/2022 17/6/2022
9-13 çalışanı T1 13/6/2022 17/6/2022 20 4 4 4 4 4

Örneğin, çalışanın bu haftanın her günü yalnızca üç saat çalışmasını ve diğer görevler için bir saat kullanmasını istiyorsanız:

UpdatedContours örnek yükü

[{

"minutes":900.0,

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

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

}]

Bu atama, Çizim Çizelgesi API Güncellemesi'nin çalıştırılmasından sonradır.

  Görev Başlangıç Tarihi Bitiş Tarihi Miktar 13/6/2022 14/6/2022 15/06/2022 16/6/2022 17/6/2022
9-13 çalışanı T1 13/6/2022 17/6/2022 15 3 3 3 3 3

Örnek senaryo

Bu senaryoda; bir proje, bir takım üyesi, dört görev ve iki kaynak ataması oluşturacaksınız. Ardından, bir görevi güncelleştirecek, projeyi güncelleştirecek, kaynak atama konturunu güncelleştirecek, bir görevi silecek, bir kaynak atamasını silecek ve bir görev bağımlılığı oluşturacaksınız.

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

Ek örnekler

#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