bc-code-intelligence-mcp
Version:
BC Code Intelligence MCP Server - Complete Specialist Bundle with AI-driven expert consultation, seamless handoffs, and context-preserving workflows
356 lines (295 loc) • 14.4 kB
Markdown
# FieldError Default Messages - AL Code Sample
## Empty Field Default Messages
```al
table 50200 "Document Header"
{
fields
{
field(1; "Document Type"; Enum "Sales Document Type") { }
field(2; "Document No."; Code[20]) { }
field(3; "Customer No."; Code[20]) { }
field(10; "Posting Date"; Date) { }
field(20; "Amount"; Decimal) { }
field(30; "Currency Code"; Code[10]) { }
field(40; "Description"; Text[100]) { }
}
keys
{
key(PK; "Document Type", "Document No.") { Clustered = true; }
}
procedure DemonstrateEmptyFieldMessages()
begin
// For empty Code field - generates specific "must have a value" message
"Customer No." := '';
// Output: "Customer No. must have a value in Document Header Document Type='Order', Document No.='DOC001'."
FieldError("Customer No.");
// For zero Date field - generates specific "must be filled in" message
"Posting Date" := 0D;
// Output: "Posting Date must be filled in in Document Header Document Type='Order', Document No.='DOC001'."
FieldError("Posting Date");
// For zero Decimal field - generates "must not be 0" message
Amount := 0;
// Output: "Amount must not be 0 in Document Header Document Type='Order', Document No.='DOC001'."
FieldError(Amount);
// For empty Text field - generates "must have a value" message
Description := '';
// Output: "Description must have a value in Document Header Document Type='Order', Document No.='DOC001'."
FieldError(Description);
end;
}
```
## Non-Empty Field Default Messages
```al
table 50201 "Validation Examples"
{
fields
{
field(1; "Entry No."; Integer) { }
field(10; "Status"; Option) { OptionMembers = " ",Open,Released,Closed; }
field(20; "Item No."; Code[20]) { }
field(30; "Quantity"; Decimal) { }
field(40; "Unit Price"; Decimal) { }
}
keys
{
key(PK; "Entry No.") { Clustered = true; }
}
procedure DemonstrateNonEmptyFieldMessages()
begin
"Entry No." := 1001;
"Item No." := 'ITEM001';
Quantity := 5.5;
"Unit Price" := 100.00;
Status := Status::Released;
// For non-empty Code field - generates "is not valid" message
// Output: "Item No. ITEM001 is not valid in Validation Examples Entry No.='1001'."
FieldError("Item No.");
// For non-zero Decimal field - generates "is not valid" message
// Output: "Quantity 5.5 is not valid in Validation Examples Entry No.='1001'."
FieldError(Quantity);
// For non-zero Option field - generates "is not valid" message
// Output: "Status Released is not valid in Validation Examples Entry No.='1001'."
FieldError(Status);
end;
}
```
## Record Context in Error Messages
```al
table 50202 "Customer Transaction"
{
fields
{
field(1; "Customer No."; Code[20]) { }
field(2; "Transaction Date"; Date) { }
field(3; "Transaction No."; Code[20]) { }
field(10; "Amount"; Decimal) { }
field(20; "Currency Code"; Code[10]) { }
}
keys
{
key(PK; "Customer No.", "Transaction Date", "Transaction No.") { Clustered = true; }
}
procedure DemonstrateRecordContextMessages()
begin
"Customer No." := 'CUST001';
"Transaction Date" := Today;
"Transaction No." := 'TXN001';
Amount := 0;
// BC automatically includes primary key fields in error message
// Output: "Amount must not be 0 in Customer Transaction Customer No.='CUST001', Transaction Date='01/28/25', Transaction No.='TXN001'."
FieldError(Amount);
// Even with custom message, record context is automatically included
// Output: "Amount cannot be zero for transactions in Customer Transaction Customer No.='CUST001', Transaction Date='01/28/25', Transaction No.='TXN001'."
FieldError(Amount, 'cannot be zero for transactions');
end;
}
```
## Default vs Custom Message Comparison
```al
codeunit 50200 "Message Comparison Examples"
{
procedure CompareDefaultAndCustomMessages()
var
SalesHeader: Record "Sales Header";
Customer: Record Customer;
begin
SalesHeader."Document Type" := SalesHeader."Document Type"::Order;
SalesHeader."No." := 'SO001';
SalesHeader."Sell-to Customer No." := '';
// DEFAULT MESSAGE EXAMPLES
// Empty field default message
// "Sell-to Customer No. must have a value in Sales Header Document Type='Order', No.='SO001'."
// SalesHeader.FieldError("Sell-to Customer No.");
// Non-empty field default message
SalesHeader."Sell-to Customer No." := 'INVALID';
// "Sell-to Customer No. INVALID is not valid in Sales Header Document Type='Order', No.='SO001'."
// SalesHeader.FieldError("Sell-to Customer No.");
// CUSTOM MESSAGE EXAMPLES
// Custom message with empty field
SalesHeader."Sell-to Customer No." := '';
// "Sell-to Customer No. is required for sales orders in Sales Header Document Type='Order', No.='SO001'."
// SalesHeader.FieldError("Sell-to Customer No.", 'is required for sales orders');
// Custom message with non-empty field
SalesHeader."Sell-to Customer No." := 'BLOCKED';
// "Sell-to Customer No. customer is blocked in Sales Header Document Type='Order', No.='SO001'."
// SalesHeader.FieldError("Sell-to Customer No.", 'customer is blocked');
// Complex custom message with context
if not Customer.Get(SalesHeader."Sell-to Customer No.") then
// "Sell-to Customer No. does not exist in customer master in Sales Header Document Type='Order', No.='SO001'."
SalesHeader.FieldError("Sell-to Customer No.", 'does not exist in customer master');
end;
}
```
## Real-World Message Generation Scenarios
```al
codeunit 50201 "Real-World Validation Messages"
{
// Scenario 1: Document Processing Validation
procedure ValidateDocumentForPosting(var SalesHeader: Record "Sales Header")
begin
// Required field validations - using default messages for consistency
if SalesHeader."Sell-to Customer No." = '' then
// "Sell-to Customer No. must have a value in Sales Header..."
SalesHeader.FieldError("Sell-to Customer No.");
if SalesHeader."Posting Date" = 0D then
// "Posting Date must be filled in in Sales Header..."
SalesHeader.FieldError("Posting Date");
// Business rule validations - using custom messages for clarity
if SalesHeader."Posting Date" > WorkDate() + 30 then
// "Posting Date cannot be more than 30 days in future in Sales Header..."
SalesHeader.FieldError("Posting Date", 'cannot be more than 30 days in future');
end;
// Scenario 2: Setup Validation with Detailed Messages
procedure ValidateCustomerSetup(var Customer: Record Customer)
var
PostCode: Record "Post Code";
begin
// Default message for required field
if Customer."No." = '' then
// "No. must have a value in Customer..."
Customer.FieldError("No.");
// Custom message for business logic
if Customer."Credit Limit (LCY)" < 0 then
// "Credit Limit (LCY) cannot be negative in Customer No.='CUST001'."
Customer.FieldError("Credit Limit (LCY)", 'cannot be negative');
// Validation with lookup and detailed custom message
if (Customer."Post Code" <> '') and not PostCode.Get(Customer."Post Code", Customer.City) then
// "Post Code is not valid for the specified city in Customer No.='CUST001'."
Customer.FieldError("Post Code", 'is not valid for the specified city');
end;
// Scenario 3: Line Validation with Context
procedure ValidateSalesLine(var SalesLine: Record "Sales Line")
var
Item: Record Item;
AvailableInventory: Decimal;
begin
// Standard required field validation
if SalesLine."No." = '' then
// "No. must have a value in Sales Line Document Type='Order', Document No.='SO001', Line No.='10000'."
SalesLine.FieldError("No.");
// Business logic with calculated values in message
if SalesLine.Type = SalesLine.Type::Item then begin
Item.Get(SalesLine."No.");
Item.CalcFields(Inventory);
AvailableInventory := Item.Inventory;
if SalesLine.Quantity > AvailableInventory then
// "Quantity exceeds available inventory (50 units available) in Sales Line..."
SalesLine.FieldError(Quantity,
StrSubstNo('exceeds available inventory (%1 units available)', AvailableInventory));
end;
// Price validation with business context
if SalesLine."Unit Price" <= 0 then
// "Unit Price must be positive for sales transactions in Sales Line..."
SalesLine.FieldError("Unit Price", 'must be positive for sales transactions');
end;
}
```
## Message Formatting Rules and Patterns
```al
codeunit 50202 "Message Formatting Examples"
{
procedure DemonstrateFormattingRules()
var
Item: Record Item;
PurchaseLine: Record "Purchase Line";
begin
Item."No." := 'ITEM001';
// RULE 1: Default messages automatically include field value for non-empty fields
Item.Description := 'Invalid Description Content';
// "Description Invalid Description Content is not valid in Item No.='ITEM001'."
// Item.FieldError(Description);
// RULE 2: Custom messages start with lowercase (BC convention)
// CORRECT:
// "Description must not contain special characters in Item No.='ITEM001'."
// Item.FieldError(Description, 'must not contain special characters');
// INCORRECT (would still work but doesn't follow BC conventions):
// Item.FieldError(Description, 'Must not contain special characters');
// RULE 3: Record context automatically appended using primary key
PurchaseLine."Document Type" := PurchaseLine."Document Type"::Order;
PurchaseLine."Document No." := 'PO001';
PurchaseLine."Line No." := 10000;
PurchaseLine.Quantity := -5;
// "Quantity cannot be negative in Purchase Line Document Type='Order', Document No.='PO001', Line No.='10000'."
PurchaseLine.FieldError(Quantity, 'cannot be negative');
// RULE 4: Multiple primary key fields included in order
// All key fields shown in error context for complete record identification
end;
procedure DemonstrateMessageCustomization()
var
Customer: Record Customer;
CreditLimit: Decimal;
begin
Customer."No." := 'CUST001';
Customer."Credit Limit (LCY)" := 5000;
CreditLimit := 10000; // Required limit for this customer
// Pattern: Specific business context in custom messages
// "Credit Limit (LCY) insufficient for customer risk category (minimum 10000 required) in Customer No.='CUST001'."
Customer.FieldError("Credit Limit (LCY)",
StrSubstNo('insufficient for customer risk category (minimum %1 required)', CreditLimit));
// Pattern: Action guidance in custom messages
// "Credit Limit (LCY) requires manager approval for amounts above 50000 in Customer No.='CUST001'."
Customer.FieldError("Credit Limit (LCY)", 'requires manager approval for amounts above 50000');
// Pattern: Reference to related data in custom messages
// "Credit Limit (LCY) conflicts with existing payment terms (Net 60) in Customer No.='CUST001'."
Customer.FieldError("Credit Limit (LCY)", 'conflicts with existing payment terms (Net 60)');
end;
}
```
## Advanced Default Message Scenarios
```al
table 50203 "Advanced Message Examples"
{
fields
{
field(1; "Primary Key"; Code[20]) { }
field(10; "Boolean Field"; Boolean) { }
field(20; "DateTime Field"; DateTime) { }
field(30; "GUID Field"; Guid) { }
field(40; "Blob Field"; Blob) { }
}
procedure DemonstrateAdvancedDefaultMessages()
begin
"Primary Key" := 'TEST001';
// Boolean field default messages
"Boolean Field" := false;
// "Boolean Field No is not valid in Advanced Message Examples Primary Key='TEST001'."
// FieldError("Boolean Field");
"Boolean Field" := true;
// "Boolean Field Yes is not valid in Advanced Message Examples Primary Key='TEST001'."
// FieldError("Boolean Field");
// DateTime field default messages
"DateTime Field" := 0DT;
// "DateTime Field must be filled in in Advanced Message Examples Primary Key='TEST001'."
// FieldError("DateTime Field");
"DateTime Field" := CurrentDateTime;
// "DateTime Field 01/28/25 09:30:00 is not valid in Advanced Message Examples Primary Key='TEST001'."
// FieldError("DateTime Field");
// GUID field default messages
Clear("GUID Field");
// "GUID Field must have a value in Advanced Message Examples Primary Key='TEST001'."
// FieldError("GUID Field");
"GUID Field" := CreateGuid();
// "GUID Field {12345678-1234-1234-1234-123456789012} is not valid in Advanced Message Examples Primary Key='TEST001'."
// FieldError("GUID Field");
end;
}