bc-code-intelligence-mcp
Version:
BC Code Intelligence MCP Server - Complete Specialist Bundle with AI-driven expert consultation, seamless handoffs, and context-preserving workflows
239 lines (203 loc) • 6.99 kB
Markdown
# AL Lonely Repeat Pattern - Code Examples
## Good Examples
### Proper Repeat-Until Loop Structure
```al
procedure ProcessCustomerRecords()
var
Customer: Record Customer;
ProcessedCount: Integer;
begin
ProcessedCount := 0;
if Customer.FindSet() then
repeat
// Process each customer record
if not Customer.Blocked then begin
Customer."Last Contact Date" := Today;
Customer.Modify();
ProcessedCount += 1;
end;
until Customer.Next() = 0; // Proper until condition
Message('Processed %1 customer records', ProcessedCount);
end;
```
### Repeat Loop with Error Handling and Exit Conditions
```al
procedure SafeDataProcessing()
var
DataRecord: Record "Data Processing Queue";
MaxAttempts: Integer;
AttemptCount: Integer;
ProcessingComplete: Boolean;
begin
MaxAttempts := 5;
AttemptCount := 0;
ProcessingComplete := false;
if DataRecord.FindSet() then
repeat
AttemptCount += 1;
// Process with error handling
if TryProcessRecord(DataRecord) then begin
ProcessingComplete := true;
DataRecord."Processing Status" := DataRecord."Processing Status"::Completed;
DataRecord.Modify();
end else begin
DataRecord."Error Count" += 1;
DataRecord.Modify();
end;
until (DataRecord.Next() = 0) or (AttemptCount >= MaxAttempts) or ProcessingComplete;
if not ProcessingComplete then
Error('Data processing failed after %1 attempts', MaxAttempts);
end;
```
### Complex Repeat Loop with Multiple Exit Conditions
```al
procedure BatchOrderProcessing()
var
SalesHeader: Record "Sales Header";
ProcessingStartTime: Time;
MaxProcessingTime: Duration;
OrdersProcessed: Integer;
BatchSize: Integer;
begin
ProcessingStartTime := Time;
MaxProcessingTime := 300000; // 5 minutes in milliseconds
OrdersProcessed := 0;
BatchSize := 100;
SalesHeader.SetRange("Document Type", SalesHeader."Document Type"::Order);
SalesHeader.SetRange(Status, SalesHeader.Status::Open);
if SalesHeader.FindSet() then
repeat
// Process individual order
if ProcessSingleOrder(SalesHeader) then
OrdersProcessed += 1;
// Update progress every 10 orders
if OrdersProcessed mod 10 = 0 then
UpdateProgressIndicator(OrdersProcessed);
until (SalesHeader.Next() = 0) or
(OrdersProcessed >= BatchSize) or
(Time - ProcessingStartTime > MaxProcessingTime);
Message('Batch processing complete. Processed %1 orders', OrdersProcessed);
end;
```
### Repeat Loop with Data Validation
```al
procedure ValidateAndProcessItems()
var
Item: Record Item;
ValidationErrors: Integer;
TotalItems: Integer;
begin
ValidationErrors := 0;
TotalItems := 0;
Item.SetRange(Blocked, false);
if Item.FindSet() then
repeat
TotalItems += 1;
// Validate each item
if not ValidateItemData(Item) then begin
ValidationErrors += 1;
LogValidationError(Item."No.");
end else begin
Item."Last Validation Date" := Today;
Item.Modify();
end;
until Item.Next() = 0;
if ValidationErrors > 0 then
Message('Validation complete: %1 errors found out of %2 items', ValidationErrors, TotalItems)
else
Message('All %1 items validated successfully', TotalItems);
end;
```
## Bad Examples
### Lonely Repeat Without Until
```al
procedure LonelyRepeat()
var
Customer: Record Customer;
begin
if Customer.FindSet() then
repeat
Customer."Last Contact Date" := Today;
Customer.Modify();
// Missing until condition - this creates an infinite loop!
Message('Processing complete'); // This will never execute
end;
```
### Unreachable Until Condition
```al
procedure UnreachableUntil()
var
Counter: Integer;
MaxCount: Integer;
begin
Counter := 1;
MaxCount := 10;
repeat
Message('Processing item %1', Counter);
// Counter is never incremented - until condition can never be met
until Counter > MaxCount; // This creates an infinite loop
end;
```
### Complex Until Logic That Obscures Termination
```al
procedure ObscureTermination()
var
SalesLine: Record "Sales Line";
ComplexCondition: Boolean;
AnotherCondition: Boolean;
YetAnotherCondition: Boolean;
begin
if SalesLine.FindSet() then
repeat
// Complex processing logic
ComplexCondition := (SalesLine.Quantity > 0) and (SalesLine."Unit Price" > 0);
AnotherCondition := (SalesLine."Line Discount %" < 50) or (SalesLine.Type = SalesLine.Type::Item);
YetAnotherCondition := not ((SalesLine."Shipment Date" = 0D) and (SalesLine."Qty. to Ship" > 0));
ProcessSalesLine(SalesLine);
until (SalesLine.Next() = 0) and ComplexCondition and AnotherCondition and YetAnotherCondition; // Poor: complex until condition
end;
```
### No Loop Variable Progression
```al
procedure NoProgression()
var
ItemNo: Code[20];
Item: Record Item;
FoundItem: Boolean;
begin
ItemNo := 'ITEM001';
FoundItem := false;
repeat
if Item.Get(ItemNo) then begin
FoundItem := true;
ProcessItem(Item);
end;
// ItemNo never changes - if item doesn't exist, this becomes infinite loop
until FoundItem;
end;
```
### Missing Error Handling for Loop Failures
```al
procedure NoErrorHandling()
var
DataRecord: Record "Processing Queue";
ProcessingSuccess: Boolean;
begin
ProcessingSuccess := false;
if DataRecord.FindSet() then
repeat
// Processing that might fail
ProcessRecord(DataRecord); // No error handling
ProcessingSuccess := true;
until DataRecord.Next() = 0;
// No handling for case where processing fails
Message('Processing complete');
end;
```
## Best Practices
1. **Always pair repeat with until** - never leave repeat statements incomplete
2. **Ensure until conditions can be met** - validate that loop variables progress toward termination
3. **Include multiple exit strategies** when appropriate (time limits, error counts, batch sizes)
4. **Use clear, simple until conditions** that are easy to understand and verify
5. **Add error handling** to prevent infinite loops from processing failures
6. **Validate loop progression** to ensure forward movement toward termination condition