พัฒนาเทมเพลตโครงการด้วยฟังก์ชั่น Copy Project

นําไปใช้กับ: การดําเนินการโครงการรวมกับ ERP, Project Operations Core

Dynamics 365 Project Operations สนับสนุนความสามารถในการคัดลอกโครงการ และแปลงกลับการกําหนดใดๆ กลับไปยังทรัพยากรทั่วไปที่แสดงบทบาท ใช้ฟังก์ชันการทํางานนี้เพื่อสร้างเทมเพลตโครงการพื้นฐาน

เมื่อคุณเลือก คัดลอกโครงการ ระบบจะอัปเดตสถานะของโครงการเป้าหมาย ใช้ คำอธิบายรายการของสถานะ เพื่อพิจารณาว่าการคัดลอกเสร็จสมบูรณ์เมื่อใด การเลือก คัดลอกโครงการ จะอัปเดตวันที่เริ่มต้นของโครงการเป็นวันที่เริ่มต้นปัจจุบันถ้าระบบตรวจไม่พบวันที่เป้าหมายในเอนทิตีโครงการเป้าหมาย

หากต้องการเพิ่มคอลัมน์เพิ่มเติมเพื่อคัดลอกจากเอนทิตีโครงการ ให้เพิ่มคอลัมน์เหล่านั้นไปยังมุมมอง คัดลอกคอลัมน์โครงการ ในเอนทิตีโครงการ จากนั้นเรียกใช้ CopyProjectEntityAttributesRequest API ผ่านปลั๊กอินหรือโฟลว์ API นี้คัดลอกข้อมูลในคอลัมน์ในมุมมอง คัดลอกคอลัมน์โครงการ จากโครงการต้นทางที่ระบุไปยังโครงการเป้าหมาย คุณต้องเรียกใช้ API นี้แยกต่างหากจากการเรียก CopyProjectV3 หรือ V4 API

เมื่อต้องการเพิ่มคอลัมน์เพิ่มเติมเพื่อคัดลอกจากเอนทิตี งานโครงการ ให้เพิ่มคอลัมน์เหล่านั้นไปยังมุมมอง คัดลอกคอลัมน์งานโครงการ บนตาราง งานโครงการ เพื่อให้แน่ใจว่าคอลัมน์จะถูกคัดลอกไปยังงานโครงการใหม่เมื่อคุณเรียกใช้งาน CopyProjectV3 หรือ V4

การดำเนินการคัดลอกโครงการ v3 แบบกำหนดเอง

ชื่อ

msdyn_CopyProjectV3

พารามิเตอร์ข้อมูลป้อนเข้า

มีพารามิเตอร์ข้อมูลป้อนเข้าสามรายการ:

  • ReplaceNamedResources หรือ ClearTeamsAndAssignments – ตั้งค่าเฉพาะหนึ่งในตัวเลือกเหล่านี้ อย่าตั้งค่าทั้งคู่

    • {"ReplaceNamedResources":true} – ลักษณะการทํางานเริ่มต้นสําหรับ Project Operations แทนที่ทรัพยากรที่มีชื่อใด ๆ ด้วยทรัพยากรทั่วไป ข้อยกเว้นเพียงอย่างเดียวคือการมอบหมายให้กับผู้จัดการโครงการในโครงการต้นทางที่ได้รับการมอบหมายให้กับผู้จัดการโครงการของโครงการเป้าหมาย (จำเป็นต้องใช้ทรัพยากรที่ระบุชื่อ และไม่ใช่ทรัพยากรทั่วไป)
    • {"ClearTeamsAndAssignments":true} – ลักษณะการทำงานเริ่มต้นสำหรับ Project for the Web ลบการมอบหมายและสมาชิกทีมทั้งหมด
  • SourceProject – การอ้างอิงเอนทิตีของโครงการต้นทางที่จะคัดลอก อย่าตั้งค่าพารามิเตอร์นี้เป็นค่า null

  • Target – การอ้างอิงเอนทิตีของโครงการเป้าหมายในการคัดลอก อย่าตั้งค่าพารามิเตอร์นี้เป็นค่า null

ตารางต่อไปนี้แสดงสรุปข้อมูลของพารามิเตอร์สามรายการ

พารามิเตอร์ ชนิด มูลค่า
ReplaceNamedResources แบบบูลีน จริง หรือ เท็จ
ClearTeamsAndAssignments แบบบูลีน จริง หรือ เท็จ
SourceProject การอ้างอิงเอนทิตี โครงการต้นทาง
เป้าหมาย การอ้างอิงเอนทิตี โครงการเป้าหมาย

สำหรับค่าเริ่มต้นเกี่ยวกับการดำเนินการเพิ่มเติม ดูที่ ใช้การดำเนินการ Web API

การตรวจสอบความถูกต้อง

ระบบจะดําเนินการตรวจสอบความถูกต้องต่อไปนี้:

  1. ซึ่งจะตรวจสอบค่า null และดึงข้อมูลโครงการต้นทางและเป้าหมายเพื่อยืนยันว่ามีโครงการทั้งสองโครงการอยู่ในองค์กรหรือไม่

  2. ซึ่งตรวจสอบว่าโครงการเป้าหมายถูกต้องสําหรับการคัดลอกโดยการตรวจสอบเงื่อนไขต่อไปนี้:

    • โครงการเป็นโครงการใหม่และไม่มีกิจกรรมก่อนหน้า รวมถึงการเลือกแท็บงาน
    • โครงการไม่มีสําเนาก่อนหน้า ไม่มีการร้องขอการนําเข้าในโครงการนี้ และโครงการไม่มีสถานะล้มเหลว
  3. การดำเนินการนี้ไม่ได้ถูกเรียกโดยใช้ HTTP

การดำเนินการ คัดลอกโครงการ v4 แบบกำหนดเอง

ชื่อ

msdyn_CopyProjectV4

พารามิเตอร์ข้อมูลป้อนเข้า

ใช้พารามิเตอร์อินพุตห้ารายการ:

  • SourceProject – การอ้างอิงเอนทิตีของโครงการต้นทางที่จะคัดลอก อย่าตั้งค่าพารามิเตอร์นี้เป็นค่า null

  • Target – การอ้างอิงเอนทิตีของโครงการเป้าหมายในการคัดลอก อย่าตั้งค่าพารามิเตอร์นี้เป็นค่า null

  • TeamMemberOption – ตัวเลือกสำหรับการคัดลอกสมาชิกในทีมไปยังโครงการเป้าหมาย อย่าตั้งค่าพารามิเตอร์นี้เป็นค่า null

    • 0 – ไม่ต้องคัดลอกสมาชิกในทีม
    • 1 – คัดลอกสมาชิกในทีมเป็นทรัพยากรทั่วไป (ทำงานเหมือนกับใน V3)
    • 2 – คัดลอกสมาชิกในทีมไปยังทรัพยากรที่มีชื่อหรือทั่วไปที่ระบุ
  • TeamMembers – คอลเลกชันเอนทิตีของสมาชิกในทีมที่มีชื่อหรือทั่วไปเพื่อแทนที่สมาชิกในทีมที่มีอยู่ ตั้งค่าพารามิเตอร์นี้เป็น null ถ้าคุณเลือก 0 หรือ 1 สําหรับพารามิเตอร์ TeamMemberOption อย่าตั้งค่าพารามิเตอร์นี้เป็น null ถ้าคุณเลือก 2

  • TeamMembersMapping – พจนานุกรมสตริง JavaScript Object Notation (JSON) ที่แมปสมาชิกในทีมจากโครงการต้นทางไปยังโครงการเป้าหมาย สำหรับแต่ละรายการ คีย์คือค่า ProjectTeamID ของสมาชิกในทีมโครงการต้นทาง และค่าคือ ProjectTeamID ของสมาชิกในทีมโครงการเป้าหมาย การมอบหมายทั้งหมดในโครงการต้นทางของสมาชิกในทีมโครงการต้นทางที่ระบุจะถูกมอบหมายให้กับสมาชิกในทีมโครงการเป้าหมายที่ระบุในโครงการเป้าหมาย สมาชิกในทีมโครงการต้นทางหลายรายสามารถแมปกับสมาชิกในทีมโครงการเป้าหมายหนึ่งคน ตั้งค่าพารามิเตอร์นี้เป็น null ถ้าคุณเลือก 0 หรือ 1 สําหรับพารามิเตอร์ TeamMemberOption ซึ่งเป็นทางเลือกถ้าคุณเลือกที่ 2

ตารางต่อไปนี้แสดงสรุปข้อมูลของพารามิเตอร์ห้ารายการ

พารามิเตอร์ ชนิด มูลค่า
SourceProject การอ้างอิงเอนทิตี โครงการต้นทาง
เป้าหมาย การอ้างอิงเอนทิตี โครงการเป้าหมาย
TeamMemberOption ชุดตัวเลือก 0, 1 หรือ 2
TeamMembers ชุดเอนทิตี สมาชิกในทีมที่มีชื่อหรือทั่วไปสำหรับโครงการเป้าหมาย
TeamMembersMapping สตริง Json พจนานุกรมที่แมปสมาชิกในทีมจากโครงการต้นทางไปยังโครงการเป้าหมาย

สำหรับค่าเริ่มต้นเกี่ยวกับการดำเนินการเพิ่มเติม ดูที่ ใช้การดำเนินการ Web API

การตรวจสอบความถูกต้อง

ระบบจะดําเนินการตรวจสอบความถูกต้องต่อไปนี้:

  1. ซึ่งจะตรวจสอบค่า null และดึงข้อมูลโครงการต้นทางและเป้าหมายเพื่อยืนยันว่ามีโครงการทั้งสองโครงการอยู่ในองค์กรหรือไม่

  2. ซึ่งจะตรวจสอบว่าสมาชิกทุกคนในทีมทั้งในพารามิเตอร์ TeamMembers และพารามิเตอร์ TeamMemberMapping ถูกต้องหรือไม่

  3. ซึ่งตรวจสอบว่าโครงการเป้าหมายถูกต้องสําหรับการคัดลอกโดยการตรวจสอบเงื่อนไขต่อไปนี้:

    • โครงการเป็นโครงการใหม่และไม่มีกิจกรรมก่อนหน้า รวมถึงการเลือกแท็บงาน
    • โครงการไม่มีสําเนาก่อนหน้า ไม่มีการร้องขอการนําเข้าในโครงการนี้ และโครงการไม่มีสถานะล้มเหลว
  4. ซึ่งจะตรวจสอบว่าไม่มีการเรียกใช้การดําเนินการโดยใช้ HTTP

ระบุช่องที่จะคัดลอก

เมื่อคุณเรียกใช้การดําเนินการ คัดลอกโครงการ จะตรวจสอบมุมมองโครงการ คัดลอกคอลัมน์โครงการ เพื่อกําหนดเขตข้อมูลที่จะคัดลอกเมื่อคัดลอกโครงการ

ตัวอย่าง

ตัวอย่างต่อไปนี้แสดงวิธีการเรียกการดำเนินการที่กำหนดเอง CopyProjectV3 ด้วยชุดพารามิเตอร์ removeNamedResources

{
    using System;
    using System.Runtime.Serialization;
    using Microsoft.Xrm.Sdk;
    using Newtonsoft.Json;

    public class CopyProjectSample
    {
        private IOrganizationService organizationService;

        public CopyProjectSample(IOrganizationService organizationService)
        {
            this.organizationService = organizationService;
        }

        public void SampleRun()
        {
            // Example source project GUID
            Guid sourceProjectId = new Guid("11111111-1111-1111-1111-111111111111");
            var sourceProject = new Entity("msdyn_project", sourceProjectId);

            Entity targetProject = new Entity("msdyn_project");
            targetProject["msdyn_subject"] = "Example Project - Copy";
            targetProject.Id = organizationService.Create(targetProject);

            CallCopyProjectAPI(sourceProject.ToEntityReference(), targetProject.ToEntityReference(), copyOption, true, false);
            Console.WriteLine("Done ...");
        }

        private void CallCopyProjectAPI(EntityReference sourceProject, EntityReference TargetProject, bool replaceNamedResources = true, bool clearTeamsAndAssignments = false)
        {
            OrganizationRequest req = new OrganizationRequest("msdyn_CopyProjectV3");
            req["SourceProject"] = sourceProject;
            req["Target"] = TargetProject;

            if (replaceNamedResources)
            {
                req["ReplaceNamedResources"] = true;
            }
            else
            {
                req["ClearTeamsAndAssignments"] = true;
            }

            OrganizationResponse response = organizationService.Execute(req);
        }
    }
}

ตัวอย่าง

ตัวอย่างต่อไปนี้แสดงวิธีการเรียกการดำเนินการ CopyProjectV4 แบบกำหนดเอง โดยที่พารามิเตอร์ TeamMemberOption ตั้งค่าเป็น 2 และทั้งค่า TeamMembers และ TeamMembersMapping จะถูกส่ง

/* To simplify, the sample code below does not contain code to setup source project, create target project, etc.
 * The sample code demonstrates calling Copy Project V4 with option to pass in team members for creation and mapping.
 * 
 * IN THE BELOW CODE EXAMPLE:
 * sourceProjectRef is the EntityReference of the source project to copy.
 * targetProjectRef is the target project, target project must be created before calling the API.
 * Target project should be created empty without doing any further setup like adding team members, tasks, etc.
 * toBeCreatedTargetTeamMembers is an EntityCollection of team members to be created in the target project
 * as part of the copy and mapping.
 * teamMembersMapping is a Dictionary<Guid, Guid> of the team member mapping between source and target project.
 * Key is the ID of a source team member; Value is the ID of the target team member.
 * */

var request = new OrganizationRequest("msdyn_CopyProjectV4");
request["Target"] = targetProjectRef;
request["SourceProject"] = sourceProjectRef;
request["TeamMemberOption"] = new OptionSetValue(2); // 0: None, 1: CopyAsGeneric, 2: Specify
request["TeamMembers"] = toBeCreatedTargetTeamMembers;
request["TeamMembersMapping"] = SerializeJson(teamMembersMapping); // See implementation of SerializeJson() method below

OrganizationService.Execute(request);

/* Sample serialized json for team member mapping:
'[
{"Key":"a2cee002-cca1-41a4-8821-09d8327741e9","Value":"32c55ac0-06d6-4809-bcdb-566635576e1e"},
{"Key":"b2cee002-cca1-41a4-8821-09d8327741e0","Value":"42c55ac0-06d6-4809-bcdb-566635576e10"},
]'
*/
string SerializeJson<T>(T obj)
{
    var serializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof(T));

    using (Stream stream = new MemoryStream())
    {
        serializer.WriteObject(stream, obj);
        stream.Position = 0;

        StreamReader streamReader = new StreamReader(stream);
        return streamReader.ReadToEnd();
    }
}

/* Optional code to wait for the copy to complete as the main work is done in Async service.
 * To see details on error, go to PSS Error Logs and/or Settings > System Jobs.
 * */
EnsureProjectCopySuccessful(targetProjectRef);

void EnsureProjectCopySuccessful(EntityReference project)
{
    var start = DateTime.Now;

    do
    {
        Thread.Sleep(TimeSpan.FromSeconds(10));
        var updatedProject = OrganizationService.Retrieve(Project.EntityLogicalName, project.Id, new ColumnSet(Project.AttributeStatuscode))
            .ToEntity<Project>();

        if (updatedProject.statuscode.Value == (int)project_statuscode.Projectcopyfailed)
        {
            throw new Exception("Project Copy failed.");
        }
        else if (updatedProject.statuscode.Value == (int)project_statuscode.Active)
        {
            break;
        }

        if (DateTime.Now > start + TimeSpan.FromMinutes(5))
        {
            throw new TimeoutException("Copy Project timed out");
        }
    }
    while (true);
}