Dynamics 365 Business Central: การใช้งาน FlowFields และ FlowFilters ใน AL Programming

ในการพัฒนา Dynamics 365 Business Central ด้วยภาษา AL การเข้าใจ FlowFields และ FlowFilters เป็นสิ่งสำคัญมาก เพราะเป็นเครื่องมือที่ช่วยในการคำนวณและกรองข้อมูลในตารางอย่างมีประสิทธิภาพ บทความนี้จะอธิบายรายละเอียดเกี่ยวกับ FlowFields และ FlowFilters พร้อมตัวอย่างการใช้งาน

FlowFields คืออะไร?

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

  • Sum: การรวมผลรวม
  • Count: การนับจำนวนรายการ
  • Average: การคำนวณค่าเฉลี่ย
  • Min: หาค่าน้อยสุด
  • Max: หาค่ามากสุด
  • Exist: ตรวจสอบการมีอยู่ของรายการ
  • Lookup: ค้นหาค่าในฟิลด์อื่น

1. Sum (การรวมผลรวม)

ตัวอย่าง: คำนวณยอดขายรวมของลูกค้า

การสร้าง FlowField ด้วย CalcFormula แบบ Sum

        field(50000; "Total Sales"; Decimal)
        {
            FieldClass = FlowField;
            CalcFormula = Sum("Sales Line"."Amount" where("Sell-to Customer No." = field("No.")));
            Editable = false;
        }

อธิบาย

  • Sum เป็นฟังก์ชันที่ใช้ในการรวมผลรวมของฟิลด์ที่ระบุ
  • "Sales Line"."Amount" ระบุฟิลด์ Amount จากตาราง Sales Line
  • เงื่อนไข where ("Sell-to Customer No."=field("No.")) คือการเชื่อมโยงกับลูกค้าปัจจุบัน

2. Count (การนับจำนวนรายการ)

ตัวอย่าง: นับจำนวนคำสั่งซื้อของลูกค้า

การสร้าง FlowField ด้วย CalcFormula แบบ Count

        field(50001; "Order Count"; Integer)
        {
            FieldClass = FlowField;
            CalcFormula = Count("Sales Header" where("Sell-to Customer No." = field("No."), "Document Type" = CONST(Order)));
            Caption = 'Order Count';
            Editable = false;
        }

อธิบาย

  • Count ใช้ในการนับจำนวนรายการที่ตรงกับเงื่อนไข
  • "Sales Header" คือตารางที่ใช้ในการนับ
  • เงื่อนไข ("Sell-to Customer No" = field("No."), "Document Type" = CONST(Order)) ระบุว่าต้องเป็นคำสั่งซื้อของลูกค้าปัจจุบันและเป็นประเภทเอกสาร Order

3. Average (การคำนวณค่าเฉลี่ย)

ตัวอย่าง: คำนวณค่าเฉลี่ยยอดขายต่อคำสั่งซื้อของลูกค้า

การสร้าง FlowField ด้วย CalcFormula แบบ Average

        field(50002; "Average Order Amount"; Decimal)
        {
            FieldClass = FlowField;
            CalcFormula = Average("Sales Line"."Amount" where("Sell-to Customer No." = field("No.")));
            Caption = 'Average Order Amount';
            Editable = false;
        }

อธิบาย

  • Average ใช้ในการคำนวณค่าเฉลี่ยของฟิลด์ที่ระบุ
  • ใช้เงื่อนไขเพื่อกรองข้อมูลที่เกี่ยวข้องกับลูกค้าปัจจุบัน

4. Min (หาค่าน้อยสุด)

ตัวอย่าง: หายอดขายน้อยสุดของลูกค้า

การสร้าง FlowField ด้วย CalcFormula แบบ Min

        field(50003; "Minimum Sale Amount"; Decimal)
        {
            FieldClass = FlowField;
            CalcFormula = Min("Sales Line"."Amount" where("Sell-to Customer No." = field("No.")));
            Caption = 'Minimum Sale Amount';
            Editable = false;
        }

อธิบาย

  • Min ใช้ในการหาค่าที่น้อยที่สุดของฟิลด์ที่ระบุ
  • ใช้สำหรับหายอดขายที่น้อยที่สุดในคำสั่งซื้อของลูกค้า

5. Max (หาค่ามากสุด)

ตัวอย่าง: หายอดขายมากสุดของลูกค้า

การสร้าง FlowField ด้วย CalcFormula แบบ Max

        field(50004; "Maximum Sale Amount"; Decimal)
        {
            FieldClass = FlowField;
            CalcFormula = Max("Sales Line"."Amount" where("Sell-to Customer No." = field("No.")));
            Caption = 'Maximum Sale Amount';
            Editable = false;
        }

อธิบาย

  • Max ใช้ในการหาค่าที่มากที่สุดของฟิลด์ที่ระบุ
  • ใช้สำหรับหายอดขายที่มากที่สุดในคำสั่งซื้อของลูกค้า

6. Exist (ตรวจสอบการมีอยู่ของรายการ)

ตัวอย่าง: ตรวจสอบว่าลูกค้ามีคำสั่งซื้อหรือไม่

การสร้าง FlowField ด้วย CalcFormula แบบ Exist

        field(50005; "Has Orders"; Boolean)
        {
            FieldClass = FlowField;
            CalcFormula = Exist("Sales Header" where("Sell-to Customer No." = field("No."), "Document Type" = CONST(Order)));
            Caption = 'Has Orders';
            Editable = false;
        }

อธิบาย

  • Exist จะคืนค่า true หากมีรายการที่ตรงกับเงื่อนไข หรือ false หากไม่มี
  • ใช้ในการตรวจสอบว่าลูกค้ามีคำสั่งซื้อหรือไม่

7. Lookup (ค้นหาค่าในฟิลด์อื่น)

ตัวอย่าง: ดึงข้อมูล Email ของผู้ติดต่อ (Contact) ที่เชื่อมโยงกับลูกค้า (Customer)

การสร้าง FlowField ด้วย CalcFormula แบบ Lookup

        field(50007; "Contact Email"; Text[100])
        {
            FieldClass = FlowField;
            CalcFormula = Lookup(Contact."E-Mail" where("No." = field("Primary Contact No.")));
            Caption = 'Contact Email';
            Editable = false;
        }

อธิบาย

  • Lookup ใช้ในการดึงค่าจากฟิลด์ในตารางอื่น
  • ในกรณีนี้ ดึงค่า E-Mail จากตาราง Contact

การคำนวณค่าใน FlowField

FlowField จะไม่คำนวณค่าอัตโนมัติ ต้องเรียกใช้เมธอด CALCFIELDS เพื่อคำนวณค่า

CustomerRec.CalcFields("Total Sales");

ตัวอย่างการใช้งาน

    var
        CustomerRec: Record Customer;
    begin
        CustomerRec.SetRange("No.", 'CUST001');
        if CustomerRec.FindFirst() then begin
            CustomerRec.CalcFields("Total Sales");
            Message('Total Sales for %1 is %2', CustomerRec."Name", CustomerRec."Total Sales");
        end;
    end;

FlowFilters คืออะไร?

FlowFilters เป็นฟิลด์ชนิดพิเศษที่ใช้ในการกรองข้อมูลสำหรับการคำนวณใน FlowFields โดยไม่เก็บข้อมูลจริง แต่ใช้เป็นตัวกรองชั่วคราว

        field(50001; "Date Filter"; Date)
        {
            FieldClass = FlowFilter;
        }

การใช้งาน FlowFilter ร่วมกับ FlowField

เมื่อเราต้องการคำนวณยอดขายในช่วงวันที่ที่กำหนด เราสามารถใช้ FlowFilter เพื่อกำหนดช่วงวันที่

ตัวอย่างการเรียกใช้ FlowFilter

        field(50000; "Total Sales"; Decimal)
        {
            FieldClass = FlowField;
            CalcFormula = Sum("Sales Line"."Amount" where("Sell-to Customer No." = field("No."),
                                                                "Requested Delivery Date" = field("Date Filter")));
            Editable = false;
        }

ตัวอย่างการใช้งาน FlowFilter ใน Code

 var
        CustomerRec: Record Customer;
        StartDate: Date;
        EndDate: Date;
    begin
        StartDate := DMY2Date(1, 1, 2024);
        EndDate := DMY2Date(31, 12, 2024);

        CustomerRec.SetRange("No.", 'CUST001');
        CustomerRec.SetRange("Date Filter", StartDate, EndDate);

        if CustomerRec.FindFirst() then begin
            CustomerRec.CalcFields("Total Sales");
            Message('Total Sales for %1 from %2 to %3 is %4',
                    CustomerRec."Name", StartDate, EndDate, CustomerRec."Total Sales");
        end;
    end;

การสร้าง Procedure ที่ใช้ FlowFields และ FlowFilters

เราสามารถสร้างฟังก์ชันเพื่อคำนวณยอดขายของลูกค้าในช่วงวันที่ที่กำหนดได้

procedure GetTotalSales(CustomerNo: Code[20]; StartDate: Date; EndDate: Date): Decimal
var
    CustomerRec: Record Customer;
begin
    CustomerRec.SetRange("No.", CustomerNo);
    CustomerRec.SetRange("Date Filter", StartDate, EndDate);

    if CustomerRec.FindFirst() then begin
        CustomerRec.CalcFields("Total Sales");
        exit(CustomerRec."Total Sales");
    end else
        exit(0);
end;

การเรียกใช้ Procedure GetTotalSales

    var
        TotalSales: Decimal;
    begin
        TotalSales := GetTotalSales('CUST001', DMY2Date(1, 1, 2023), DMY2Date(31, 12, 2023));
        Message('Total Sales is %1', TotalSales);
    end;

สรุป

FlowFields และ FlowFilters เป็นเครื่องมือที่ทรงพลังใน AL สำหรับการคำนวณและกรองข้อมูลอย่างมีประสิทธิภาพ การเข้าใจการใช้งานและวิธีการทำงานของพวกมันจะช่วยให้การพัฒนาแอปพลิเคชันเป็นไปอย่างราบรื่นและมีประสิทธิภาพมากขึ้น

Dynamics 365 Business Central: การใช้งาน ConvertDateTimeFromUTCToTimeZone ใน TypeHelper Codeunit

ในการพัฒนาแอปพลิเคชันด้วยภาษา AL สำหรับ Microsoft Dynamics 365 Business Central การจัดการกับวันที่และเวลา (DateTime) เป็นสิ่งที่สำคัญ โดยเฉพาะอย่างยิ่งเมื่อมีการใช้งานในหลายเขตเวลา (Time Zones) ฟังก์ชัน ConvertDateTimeFromUTCToTimeZone ใน TypeHelper Codeunit ช่วยให้คุณสามารถแปลงค่า DateTime จาก UTC ไปยัง Time Zone ที่ต้องการได้อย่างง่ายดาย

รายละเอียดของฟังก์ชัน

รูปแบบของฟังก์ชัน

procedure ConvertDateTimeFromUTCToTimeZone(InputDateTime: DateTime; TimeZoneTxt: Text): DateTime
var
    TimeZoneInfo: DotNet TimeZoneInfo;
    Offset: Duration;
begin
    if TimeZoneTxt = '' then
        exit(InputDateTime);

    GetUserClientTypeOffset(Offset);
    InputDateTime := InputDateTime - Offset;

    TimeZoneInfo := TimeZoneInfo.FindSystemTimeZoneById(TimeZoneTxt);
    exit(InputDateTime + TimeZoneInfo.BaseUtcOffset);
end;

พารามิเตอร์

  • InputDateTime: ค่าวันที่และเวลาในรูปแบบ UTC ที่ต้องการแปลง
  • TimeZoneTxt: ชื่อของ Time Zone ที่ต้องการแปลงไป

คำอธิบาย

ฟังก์ชันนี้จะรับค่า DateTime ในรูปแบบ UTC และแปลงไปยัง Time Zone ที่ระบุโดย TimeZoneTxt ถ้า TimeZoneTxt ว่างเปล่า ฟังก์ชันจะคืนค่า InputDateTime โดยไม่ทำการแปลง

การทำงานภายในของฟังก์ชัน

  1. ตรวจสอบ TimeZoneTxt: ถ้า TimeZoneTxt เป็นค่าว่าง (''), ฟังก์ชันจะคืนค่า InputDateTime ทันที
  2. ปรับค่า Offset ของผู้ใช้: เรียกใช้ GetUserClientTypeOffset(Offset); เพื่อปรับค่าชดเชยของเวลาตามประเภทของ Client ที่ผู้ใช้กำลังใช้งาน
  3. แปลง TimeZoneTxt เป็น TimeZoneInfo: ใช้ TimeZoneInfo.FindSystemTimeZoneById(TimeZoneTxt); เพื่อหา Time Zone ที่ตรงกับชื่อที่ระบุ
  4. คำนวณเวลาที่แปลงแล้ว: นำค่า InputDateTime ลบด้วย Offset และบวกกับ TimeZoneInfo.BaseUtcOffset เพื่อให้ได้เวลาที่ถูกต้องใน Time Zone ที่ต้องการ

ตัวอย่างการใช้งาน

ตัวอย่างที่ 1: แปลงเวลาจาก UTC ไปยัง “Pacific Standard Time”

procedure TestConvertDateTime()
var
    TypeHelper: Codeunit "Type Helper";
    InputDateTime: DateTime;
    OutputDateTime: DateTime;
    TimeZoneTxt: Text;
begin
    InputDateTime := CURRENTDATETIME;
    TimeZoneTxt := 'Pacific Standard Time';

    OutputDateTime := TypeHelper.ConvertDateTimeFromUTCToTimeZone(InputDateTime, TimeZoneTxt);

    MESSAGE('เวลาปัจจุบันใน Pacific Standard Time คือ %1', FORMAT(OutputDateTime));
end;

คำอธิบายตัวอย่าง

  • กำหนด InputDateTime เป็นเวลาปัจจุบันใน UTC โดยใช้ CURRENTDATETIME
  • กำหนด TimeZoneTxt เป็น 'Pacific Standard Time'
  • เรียกใช้ฟังก์ชัน ConvertDateTimeFromUTCToTimeZone เพื่อแปลงเวลา
  • แสดงผลลัพธ์โดยใช้ MESSAGE

สรุป

ฟังก์ชัน ConvertDateTimeFromUTCToTimeZone ใน TypeHelper Codeunit เป็นเครื่องมือที่มีประโยชน์สำหรับการแปลงเวลาจาก UTC ไปยัง Time Zone ที่ต้องการ โดยรองรับการใช้งานกับ Time Zone ต่าง ๆ ที่ระบบปฏิบัติการรองรับ การเข้าใจการทำงานและการใช้งานฟังก์ชันนี้จะช่วยให้การพัฒนาแอปพลิเคชันด้วยภาษา AL มีประสิทธิภาพมากยิ่งขึ้น

Dynamics 365 Business Central: การใช้งาน Obsolete ใน AL Programming

ในภาษา AL ที่ใช้สำหรับการพัฒนา Microsoft Dynamics 365 Business Central เรามีคุณสมบัติที่เรียกว่า Obsolete ซึ่งใช้สำหรับการทำเครื่องหมาย (mark) ว่า object หรือ element ในโค้ดของเราไม่ควรถูกใช้งานอีกต่อไป เช่น table, field, หรือ procedure

ทำไมต้องใช้ Obsolete ?

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

  • แจ้งเตือนนักพัฒนาว่าส่วนนี้จะถูกเลิกใช้งาน
  • ป้องกันการใช้งาน object ที่ไม่ต้องการ
  • จัดการกับการปรับปรุงเวอร์ชันในอนาคตอย่างมีประสิทธิภาพ

การใช้งาน Obsolete

Obsolete Attribute ประกอบด้วยสอง property หลัก

  1. ObsoleteState: กำหนดสถานะของการเลิกใช้งาน ซึ่งมีค่าสองค่า:
    • Pending: แสดงคำเตือน (warning) เมื่อมีการใช้งาน
    • Removed: แสดงข้อผิดพลาด (error) เมื่อมีการใช้งาน
  2. ObsoleteReason: ข้อความที่อธิบายเหตุผลของการเลิกใช้งาน

รูปแบบการใช้งาน

ObsoleteState = Pending|Removed;
ObsoleteReason = 'เหตุผลในการเลิกใช้งาน';

ตัวอย่างการใช้งานใน Procedure

สมมติว่าเรามี procedure ที่ต้องการเลิกใช้งาน

procedure CalculateDiscount(amount: Decimal): Decimal
var
    discountRate: Decimal;
begin
    // การคำนวณส่วนลดแบบเก่า
    discountRate := 0.1;
    exit(amount * discountRate);
end;

เราสามารถทำให้ procedure นี้ obsolete ได้ดังนี้

[Obsolete('This method is obsolete. Use NewCalculateDiscount instead.', '24.0')]
procedure CalculateDiscount(amount: Decimal): Decimal
var
    discountRate: Decimal;
begin
    discountRate := 0.1;
    exit(amount * discountRate);
end;

ในตัวอย่างนี้

  • เราใช้ [Obsolete()] attribute เพื่อทำให้ procedure นี้ obsolete
  • ข้อความ 'This method is obsolete. Use NewCalculateDiscount instead.' จะปรากฏเมื่อมีการใช้งาน procedure นี้
  • '24.0' ระบุว่า procedure นี้จะถูกเลิกใช้งานในเวอร์ชัน 24.0

ตัวอย่างการใช้งานใน Field

ถ้าเรามี field ใน table ที่ต้องการเลิกใช้งาน

fields
{
    field(1000; "Old Field"; Text[50])
    {
        ObsoleteState = Removed;
        ObsoleteReason = 'This field is no longer used.';
    }
}

ในกรณีนี้

  • เมื่อ ObsoleteState ถูกตั้งเป็น Removed การใช้งาน field นี้จะทำให้เกิด error
  • ObsoleteReason ให้ข้อมูลเพิ่มเติมแก่นักพัฒนาเกี่ยวกับเหตุผลในการเลิกใช้งาน

การจัดการกับ Obsolete Object

เมื่อ object ถูกทำเครื่องหมายเป็น obsolete

  • หยุดการใช้งานในโค้ดใหม่
  • อัปเดตโค้ดเก่าที่เรียกใช้งาน object นั้น
  • เตรียมการสำหรับการลบ object นั้นในเวอร์ชันอนาคต

สรุป

การใช้ Obsolete ในภาษา AL เป็นวิธีที่มีประสิทธิภาพในการจัดการกับการเลิกใช้งาน object หรือ element ในโค้ดของเรา ช่วยให้นักพัฒนาสามารถรับรู้และปรับปรุงโค้ดตามการเปลี่ยนแปลงได้อย่างรวดเร็ว