เป้าหมายและพื้นฐาน
เมื่อจบ: เข้าใจ object สร้าง Table/Page และอ่านตรรกะใน Codeunit
พื้นฐานที่ต้องรู้: 12
แบบฝึกหลัก: สร้างตารางและหน้าฝึก แล้วทดสอบ Procedure คำนวณ
ภาพรวมก่อนลงมือทำ
object ของ AL เชื่อมกันอย่างไร
จากข้อมูลที่บันทึกได้สู่ตรรกะที่เรียกใช้ซ้ำ
- table 50102
SBC Practice: fields และ key(PK; "No.")
- page 50102
SourceTable = "SBC Practice"; field ผูกกับ Rec
- codeunit 50100
CalculateAmount รับ Decimal และคืน Decimal
- Extension (.app)
รวม object + app.json แล้ว Package และ Publish
สิ่งที่ควรจำภาพนี้เป็นแผนที่ส่วนประกอบ ไม่ใช่ลำดับรัน: Page ฝึกข้อมูลผูก Table ส่วน Page คำนวณเรียก Codeunit
Extension และ object คืออะไร
AL คือภาษาที่ใช้พัฒนา Business Central ส่วน Extension คือแอปที่บรรจุ object และ metadata ใน app.json แล้ว Package เป็นไฟล์ .app หนึ่ง Extension มีได้หลาย object; tableextension เป็น object ชนิดหนึ่งภายในแอป ไม่ใช่ชื่อเรียกแทนแอปทั้งชุด
| Object | หน้าที่ | ตัวอย่างที่ต้องเข้าใจ |
|---|---|---|
| Table | กำหนด fields, data types, keys และกฎข้อมูล | ตาราง SBC Practice เก็บ No. และ Description |
| Page | หน้ารายการ/การ์ดที่ผู้ใช้โต้ตอบ | SourceTable ผูกกับตาราง; Rec คือ record ปัจจุบัน |
| Codeunit | รวม procedure และตรรกะที่เรียกซ้ำ | SBC Calculation คำนวณยอด |
| Table Extension | เพิ่ม fields และพฤติกรรมให้ตารางที่ขยายได้ | เพิ่ม SBC Training Note บน Customer |
| Page Extension | เพิ่ม/ปรับส่วนแสดงผลและ action บนหน้าที่ขยายได้ | แสดง Training note บน Customer Card |
| Report / Report Extension | สร้าง dataset และผลลัพธ์รายงาน / ขยายรายงานที่รองรับ | แยกข้อมูลใน dataset ออกจาก layout ที่ใช้พิมพ์ |
| Query | อ่านหรือรวมข้อมูลจาก dataitems | ข้อมูลสรุปสำหรับวิเคราะห์ ไม่ใช่หน้าจอกรอกข้อมูล |
| XMLport | กำหนดรูปแบบนำเข้า/ส่งออกข้อมูล | ไฟล์ XML หรือข้อความตาม schema |
| Enum / Enum Extension | ชุดค่าที่มีชื่อ / ขยายชุดค่าที่อนุญาต | สถานะ Open และ Completed แทนตัวเลขที่ไม่มีความหมาย |
| Interface | กำหนดสัญญา procedure ให้ codeunit นำไป implement | หลาย implementation ทำงานผ่านสัญญาเดียวกัน |
| Permission Set / Extension | กำหนดหรือเพิ่มสิทธิ์ object/data | อนุญาตเปิดหน้าฝึกและอ่านเขียนตารางฝึก |
| Profile / Page Customization | จัดประสบการณ์ตามบทบาทและปรับหน้าจอ | เลือก Role Center และปรับ UI ตามผู้ใช้ |
Trigger, Procedure และ Event ไม่ใช่ object แยก: trigger เช่น OnAction ทำงานเมื่อเกิดเหตุการณ์ที่กำหนด; procedure เป็นหน่วยงานที่เรียกใช้; event publisher/subscriber ช่วยเชื่อมตรรกะเข้ากับจุดขยายของระบบ
อ่านไฟล์ AL และชนิดข้อมูล
หนึ่งไฟล์ตัวอย่างมีหนึ่ง object เริ่มด้วยชนิด, ID และชื่อ แล้วมี properties และส่วนประกอบในวงเล็บปีกกา ชื่อที่มีช่องว่างใช้ double quotes ส่วนข้อความใช้ single quotes และ statement จบด้วย semicolon
| ชนิด / คำสั่ง | ใช้เมื่อใด |
|---|---|
| Code[20], Text[100] | รหัสและข้อความที่กำหนดความยาว |
| Integer, Decimal, Boolean, Date | จำนวนเต็ม ทศนิยม จริง/เท็จ และวันที่ |
| Record Customer | ตัวแปร record สำหรับทำงานกับตาราง Customer |
| Label | ข้อความที่รองรับการแปล เช่น error/message |
| if … then / case / foreach | เลือกเงื่อนไขและวนทำงาน |
| := / = / exit(...) | กำหนดค่า / เปรียบเทียบ / คืนค่าจาก procedure |
เรียน field ID และ object ID แยกกัน: field ID อยู่ภายในตาราง ส่วน object ID อยู่ในประเภท object และต้องอยู่ในช่วงที่โปรเจกต์อนุญาต ชื่อ Caption ที่ผู้ใช้เห็นเปลี่ยนได้โดยไม่จำเป็นต้องเปลี่ยน identity ของ schema
ลงมือสร้าง Table และ Page แรก
- จากโปรเจกต์บท 12 ที่เชื่อมระบบได้แล้ว เพิ่มไฟล์
StartBCPractice.Table.al,StartBCPractice.Page.alและStartBCPractice.PermissionSet.alจาก source ด้านล่าง - อ่าน table 50102: fields กำหนด No. และ Description; key PK ใช้ No. เพื่อแยกแต่ละ record
- อ่าน page 50102: PageType = List, SourceTable = "SBC Practice" และแต่ละ field ผูกกับ Rec จึงบันทึกข้อมูลลงตารางฝึก ไม่ใช่ตัวแปรชั่วคราว
- ตั้ง idRanges ให้ครอบคลุม 50102 และ startupObjectId เป็น 50102 จากนั้น Download Symbols → Package → publish ไปแล็บ
- ให้ผู้ดูแล assign permission set
SBC PRACTICEแก่ผู้ฝึกตามขอบเขตงาน ชุดนี้ให้สิทธิ์เฉพาะตารางและหน้าฝึก ไม่ใช่สิทธิ์พัฒนาหรือสิทธิ์ Customer ทั้งหมด - ค้น StartBC practice records เพิ่ม No. = TEST-001 และ Description = First AL record ออกจากหน้าแล้วเปิดใหม่เพื่อตรวจว่าข้อมูลยังอยู่
เกณฑ์ผ่าน: ข้อมูลคงอยู่เมื่อเปิดหน้าใหม่ และ No. เดียวกันไม่ถูกสร้างซ้ำในตาราง จากนั้นทดลองหน้าคำนวณเดิมซึ่งผูกกับตัวแปรแทน SourceTable เพื่อเปรียบเทียบความแตกต่าง
อ่านโครงสร้างก่อนคัดลอก
CalculateAmount รับ Quantity กับ UnitPrice ชนิด Decimal และคืน Decimal ชื่อพารามิเตอร์บอกความหมายชัดเจน โค้ดใช้ if เพื่อปฏิเสธค่าติดลบ ใช้ Error ยุติกรณีนั้น แล้ว exit คืนผลคูณที่ปัดไว้สองตำแหน่ง
Procedure ใน Codeunit นี้ไม่ได้ประกาศ local จึงเรียกจาก Page อื่นได้ ถ้าประกาศ local จะเรียกได้ภายใน object นั้น ใช้ Label สำหรับข้อความ error เพื่อแยกข้อความจากตรรกะ ไม่ฝังการตรวจนี้ในหน้าจอเพียงแห่งเดียว
ขั้นตอนทดลองกับหน้าฝึก
- ทำบท 12 ให้ได้โปรเจกต์ที่ Download Symbols สำเร็จ อ่าน README ชุดโค้ดและตรวจช่วง ID ก่อน
- นำ StartBCCalculation.Codeunit.al และ StartBCExercise.Page.al ไปไว้ใน src/ ไฟล์ละ object ไม่เก็บสำเนา .al ซ้ำอีกที่ในโปรเจกต์
- อ่าน codeunit ตามตัวอย่างด้านล่าง แล้วเปิด page เพื่อดู field ที่ผูกกับตัวแปรและ OnAction ที่เรียก procedure
- AL: Package ตรวจ Problems ให้ไม่มี error ก่อน Run Without Debugging ไป Sandbox
- เปิด Tell Me ค้น StartBC calculation exercise ป้อน Quantity = 2 และ Unit price = 150 แล้วเลือก Calculate จดผลในช่อง Amount
- เปลี่ยน Quantity เป็น 0 แล้วคำนวณ จากนั้นลอง -1 ตรวจข้อความผิดพลาด สังเกตว่า Amount เดิมอาจยังคงอยู่เมื่อการคำนวณใหม่ error ต้องไม่ตีความว่าเป็นผลใหม่
- บันทึกผลจริงในตารางพร้อมเวอร์ชันที่ใช้ ไม่ใช้ตาราง expected แทนหลักฐานการรัน
แบบฝึกและผลที่คาดหวัง
| Quantity | UnitPrice | คาดหวัง |
|---|---|---|
| 2 | 150 | 300 |
| 0 | 150 | 0 |
| 3 | 12.345 | 37.04 |
| -1 | 150 | error จำนวน |
| 1 | -5 | error ราคา |
ตารางเป็นผลตามตรรกะที่ตั้งใจ ยังไม่ใช่ผลจาก AL runtime ที่ StartBC ทดสอบแล้ว รูปแบบทศนิยมที่แสดงอาจต่างตาม locale
โจทย์คิด: ถ้าจะอนุญาตจำนวนติดลบเพื่อใบลดหนี้ ต้องกลับไปออกแบบกฎใด ไม่ใช่ลบ if โดยไม่ทบทวนกระบวนการ
ขอบเขตของสูตรตัวอย่าง
Round(..., 0.01) เป็นการเลือกความละเอียดสำหรับโจทย์ฝึก ไม่ใช่การอ่าน Currency/General Ledger Setup สูตรนี้ไม่รวมส่วนลด ภาษี หน่วยแปลง และ pricing engine ของ Business Central จึงไม่ใช้แทนยอดเอกสารจริง
Source AL ที่ใช้ในบทนี้
table 50102 "SBC Practice"
{
Caption = 'StartBC practice';
DataClassification = CustomerContent;
fields
{
field(1; "No."; Code[20])
{
Caption = 'No.';
NotBlank = true;
}
field(2; Description; Text[100])
{
Caption = 'Description';
}
}
keys
{
key(PK; "No.")
{
Clustered = true;
}
}
}
ดาวน์โหลด StartBCPractice.Table.al
page 50102 "SBC Practice List"
{
Caption = 'StartBC practice records';
PageType = List;
SourceTable = "SBC Practice";
ApplicationArea = All;
UsageCategory = Lists;
layout
{
area(Content)
{
repeater(Records)
{
field("No."; Rec."No.")
{
ApplicationArea = All;
ToolTip = 'Specifies the unique code for a training record.';
}
field(Description; Rec.Description)
{
ApplicationArea = All;
ToolTip = 'Specifies the description of the training record.';
}
}
}
}
}
ดาวน์โหลด StartBCPractice.Page.al
permissionset 50102 "SBC PRACTICE"
{
Assignable = true;
Caption = 'StartBC practice records';
Permissions = tabledata "SBC Practice" = RIMD,
table "SBC Practice" = X,
page "SBC Practice List" = X;
}
ดาวน์โหลด StartBCPractice.PermissionSet.al
codeunit 50100 "SBC Calculation"
{
procedure CalculateAmount(Quantity: Decimal; UnitPrice: Decimal): Decimal
begin
if Quantity < 0 then
Error(NegativeQuantityErr);
if UnitPrice < 0 then
Error(NegativePriceErr);
exit(Round(Quantity * UnitPrice, 0.01));
end;
var
NegativeQuantityErr: Label 'Quantity must be zero or greater.';
NegativePriceErr: Label 'Unit price must be zero or greater.';
}
ดาวน์โหลด StartBCCalculation.Codeunit.al
page 50101 "SBC Calculation Exercise"
{
PageType = Card;
ApplicationArea = All;
UsageCategory = Tasks;
Caption = 'StartBC calculation exercise';
layout
{
area(Content)
{
group(Inputs)
{
Caption = 'Inputs';
field(QuantityInput; Quantity)
{
ApplicationArea = All;
Caption = 'Quantity';
ToolTip = 'Specifies a nonnegative training quantity.';
}
field(PriceInput; UnitPrice)
{
ApplicationArea = All;
Caption = 'Unit price';
ToolTip = 'Specifies a nonnegative training price.';
}
field(AmountOutput; Amount)
{
ApplicationArea = All;
Caption = 'Amount';
Editable = false;
ToolTip = 'Shows the calculated training amount.';
}
}
}
}
actions
{
area(Processing)
{
action(CalculateTraining)
{
Caption = 'Calculate';
ApplicationArea = All;
Image = Calculate;
ToolTip = 'Calculates the amount from the current inputs.';
trigger OnAction()
var
Calculation: Codeunit "SBC Calculation";
begin
Amount := Calculation.CalculateAmount(Quantity, UnitPrice);
end;
}
}
}
trigger OnOpenPage()
begin
Quantity := 2;
UnitPrice := 150;
end;
var
Quantity: Decimal;
UnitPrice: Decimal;
Amount: Decimal;
}
ปัญหาพบบ่อย
- เรียก procedure จาก Page ไม่ได้
- ตรวจชื่อ codeunit/procedure การประกาศ local และ dependency ที่ Package พบ
- ไม่พบหน้าฝึกใน Tell Me
- ตรวจว่า publish แล้ว และ page มี UsageCategory/ApplicationArea รวมถึงสิทธิ์ผู้ใช้สำหรับ object ใหม่
- คัดลอกแล้ว syntax error
- ใช้ไฟล์ดาวน์โหลดโดยตรง ตรวจอักขระ quote/semicolon และอย่าวาง codeunit ซ้อนอยู่ใน page object
เอกสารอ้างอิงทางการ
อ่านประกอบและตรวจชื่อเมนู/ข้อจำกัดของเวอร์ชันที่คุณใช้ เอกสารเดิมตรวจ 22 ตุลาคม 2569; เอกสารติดตั้งและพื้นฐาน Developer ที่เพิ่มตรวจ 4 พฤศจิกายน 2569