mirror of
https://github.com/microsoft/BCQuality.git
synced 2026-10-05 14:46:55 +01:00
Add knowledge-backed AL development
Add read-only planning and repository-changing development skills so BCQuality knowledge can guide features, bug fixes, refactors, upgrades, and maintenance before the existing AL review gate runs. Track Microsoft Learn ingestion and add development and BCApps-shaped guidance evaluation fixtures. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 638b66d2-9f06-4f60-8781-808709e1485c
This commit is contained in:
parent
1a5afdc0eb
commit
56b80e6dcf
50 changed files with 11589 additions and 79 deletions
|
|
@ -0,0 +1,28 @@
|
|||
table 50603 "Sample Order Header Bad"
|
||||
{
|
||||
fields
|
||||
{
|
||||
field(1; "No."; Code[20])
|
||||
{
|
||||
DataClassification = CustomerContent;
|
||||
}
|
||||
field(2; "Document Date"; Date)
|
||||
{
|
||||
DataClassification = CustomerContent;
|
||||
}
|
||||
}
|
||||
|
||||
trigger OnInsert()
|
||||
var
|
||||
SalesSetup: Record "Sales & Receivables Setup";
|
||||
NoSeries: Codeunit "No. Series";
|
||||
begin
|
||||
"Document Date" := WorkDate();
|
||||
|
||||
if "No." = '' then begin
|
||||
SalesSetup.Get();
|
||||
SalesSetup.TestField("Order Nos.");
|
||||
"No." := NoSeries.GetNextNo(SalesSetup."Order Nos.");
|
||||
end;
|
||||
end;
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
table 50602 "Sample Order Header Good"
|
||||
{
|
||||
fields
|
||||
{
|
||||
field(1; "No."; Code[20])
|
||||
{
|
||||
DataClassification = CustomerContent;
|
||||
}
|
||||
field(2; "Document Date"; Date)
|
||||
{
|
||||
DataClassification = CustomerContent;
|
||||
}
|
||||
}
|
||||
|
||||
trigger OnInsert()
|
||||
var
|
||||
SalesSetup: Record "Sales & Receivables Setup";
|
||||
NoSeries: Codeunit "No. Series";
|
||||
begin
|
||||
if "No." = '' then begin
|
||||
SalesSetup.Get();
|
||||
SalesSetup.TestField("Order Nos.");
|
||||
"No." := NoSeries.GetNextNo(SalesSetup."Order Nos.");
|
||||
end;
|
||||
|
||||
InitRecord();
|
||||
end;
|
||||
|
||||
procedure InitRecord()
|
||||
begin
|
||||
OnBeforeInitRecord(Rec);
|
||||
"Document Date" := WorkDate();
|
||||
OnAfterInitRecord(Rec);
|
||||
end;
|
||||
|
||||
[IntegrationEvent(false, false)]
|
||||
local procedure OnBeforeInitRecord(var SampleOrderHeader: Record "Sample Order Header Good")
|
||||
begin
|
||||
end;
|
||||
|
||||
[IntegrationEvent(false, false)]
|
||||
local procedure OnAfterInitRecord(var SampleOrderHeader: Record "Sample Order Header Good")
|
||||
begin
|
||||
end;
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
bc-version: [all]
|
||||
domain: data-modeling
|
||||
keywords: [document-header, initrecord, number-series, default-values, oninsert, initialization]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# Initialize document defaults in `InitRecord` after assigning the number
|
||||
|
||||
## Description
|
||||
|
||||
Business Central document headers assign their number series first and then call an `InitRecord` procedure that owns the remaining business defaults, such as posting and document dates. Keeping that sequence and extensibility point makes initialization consistent for every creation path and lets extensions subscribe around one documented operation. Defaults scattered across page triggers or unrelated helpers can differ between UI, API, test, and background creation.
|
||||
|
||||
## Best Practice
|
||||
|
||||
In the document table's insert path, assign the document number and then call `InitRecord`. Keep the default assignments in that procedure and expose narrow before/after events when other extensions must participate.
|
||||
|
||||
See sample: `initialize-document-defaults-in-initrecord.good.al`.
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Assigning document defaults in a page trigger, or scattering them directly through `OnInsert` with no `InitRecord` boundary. Non-page creation paths can then miss the defaults, and extensions have no stable initialization hook.
|
||||
|
||||
See sample: `initialize-document-defaults-in-initrecord.bad.al`.
|
||||
|
||||
## Reference
|
||||
|
||||
[Use the InitRecord function](https://learn.microsoft.com/en-us/training/modules/use-document-standards-business-central/3-use-initrecord-function)
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
codeunit 50601 "Directed Rounding Bad"
|
||||
{
|
||||
procedure FloorAmount(Value: Decimal; Precision: Decimal): Decimal
|
||||
begin
|
||||
// For negative values, '<' rounds toward zero rather than toward negative infinity.
|
||||
exit(Round(Value, Precision, '<'));
|
||||
end;
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
codeunit 50600 "Directed Rounding Good"
|
||||
{
|
||||
procedure RoundAmount(Value: Decimal; Precision: Decimal; IncreaseMagnitude: Boolean): Decimal
|
||||
begin
|
||||
if IncreaseMagnitude then
|
||||
exit(Round(Value, Precision, '>'));
|
||||
|
||||
exit(Round(Value, Precision, '<'));
|
||||
end;
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
bc-version: [all]
|
||||
domain: data-modeling
|
||||
keywords: [round, rounding, direction, precision, negative-decimal, amount]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# `Round` direction symbols follow magnitude, not mathematical ordering
|
||||
|
||||
## Description
|
||||
|
||||
AL's `Round(Number, Precision, Direction)` uses `'>'` to round away from zero and `'<'` to round toward zero. For a negative value this reverses mathematical ordering: `Round(-1234.56789, 0.001, '<')` returns `-1234.567`, while direction `'>'` returns `-1234.568`. Code that treats the symbols as mathematical ceiling and floor produces sign-dependent amount errors, commonly on credit documents and negative adjustments.
|
||||
|
||||
## Best Practice
|
||||
|
||||
Choose the direction from the business meaning: `'>'` increases absolute magnitude and `'<'` decreases absolute magnitude for both positive and negative values. Include positive and negative cases whenever a directed rounding rule is tested.
|
||||
|
||||
See sample: `round-direction-symbols-use-magnitude.good.al`.
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Using `'<'` as a mathematical floor or `'>'` as a mathematical ceiling. The result looks correct for positive amounts but moves in the opposite mathematical direction for negative amounts.
|
||||
|
||||
See sample: `round-direction-symbols-use-magnitude.bad.al`.
|
||||
|
||||
## Reference
|
||||
|
||||
[Use the Round function](https://learn.microsoft.com/en-us/training/modules/use-document-standards-business-central/4a-use-round-function)
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
interface "I Quote Amount Bad"
|
||||
{
|
||||
procedure GetAmount(): Decimal;
|
||||
}
|
||||
|
||||
interface "I Quote Date Bad"
|
||||
{
|
||||
procedure GetDate(): Date;
|
||||
}
|
||||
|
||||
codeunit 50611 "Quote Reader Bad"
|
||||
{
|
||||
procedure GetDate(Quote: Interface "I Quote Amount Bad"): Date
|
||||
var
|
||||
DatedQuote: Interface "I Quote Date Bad";
|
||||
begin
|
||||
DatedQuote := Quote as "I Quote Date Bad";
|
||||
exit(DatedQuote.GetDate());
|
||||
end;
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
interface "I Quote Amount Good"
|
||||
{
|
||||
procedure GetAmount(): Decimal;
|
||||
}
|
||||
|
||||
interface "I Quote Date Good"
|
||||
{
|
||||
procedure GetDate(): Date;
|
||||
}
|
||||
|
||||
codeunit 50610 "Quote Reader Good"
|
||||
{
|
||||
procedure TryGetDate(Quote: Interface "I Quote Amount Good"; var QuoteDate: Date): Boolean
|
||||
var
|
||||
DatedQuote: Interface "I Quote Date Good";
|
||||
begin
|
||||
if not (Quote is "I Quote Date Good") then
|
||||
exit(false);
|
||||
|
||||
DatedQuote := Quote as "I Quote Date Good";
|
||||
QuoteDate := DatedQuote.GetDate();
|
||||
exit(true);
|
||||
end;
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
bc-version: [25..]
|
||||
domain: interfaces
|
||||
keywords: [interface, is-operator, as-operator, type-test, cast, variant, runtime-error]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# Guard optional interface casts with `is`
|
||||
|
||||
## Description
|
||||
|
||||
From runtime 14.0, AL can type-test an interface or `Variant` with `is` and cast it to another interface with `as`. The test is non-throwing, but `as` raises a runtime error when the underlying codeunit does not implement the target interface. This matters when an extended capability is optional or implementations can come from other extensions.
|
||||
|
||||
## Best Practice
|
||||
|
||||
Use `is` to establish that the value supports the target interface before using `as`. Cast directly only where the target implementation is an invariant guaranteed by the surrounding contract.
|
||||
|
||||
See sample: `guard-interface-casts-with-is.good.al`.
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Using `as` unconditionally for an optional extended interface. An otherwise valid implementation of the base interface then fails at runtime merely because it does not implement the additional contract.
|
||||
|
||||
See sample: `guard-interface-casts-with-is.bad.al`.
|
||||
|
||||
## Reference
|
||||
|
||||
[Understand type testing and casting operators for interfaces](https://learn.microsoft.com/en-us/training/modules/business-central-interfaces/type-testing)
|
||||
|
|
@ -8,9 +8,6 @@ page 50375 "Sample App Area Bad"
|
|||
{
|
||||
group(General)
|
||||
{
|
||||
// Anti-pattern: no ApplicationArea. AS0062 flags this control,
|
||||
// and it is silently hidden in the Web client for profiles whose
|
||||
// enabled areas do not already cover it.
|
||||
field("No."; Rec."No.")
|
||||
{
|
||||
ToolTip = 'Specifies the number that identifies the customer.';
|
||||
|
|
@ -23,3 +20,18 @@ page 50375 "Sample App Area Bad"
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pageextension 50377 "Customer App Area Bad" extends "Customer Card"
|
||||
{
|
||||
layout
|
||||
{
|
||||
addlast(General)
|
||||
{
|
||||
// Extension controls do not inherit ApplicationArea from the base page.
|
||||
field("Language Code Sample"; Rec."Language Code")
|
||||
{
|
||||
ToolTip = 'Specifies the language used for the customer.';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ page 50374 "Sample App Area Good"
|
|||
{
|
||||
PageType = Card;
|
||||
SourceTable = Customer;
|
||||
ApplicationArea = All;
|
||||
|
||||
layout
|
||||
{
|
||||
area(Content)
|
||||
|
|
@ -10,12 +12,10 @@ page 50374 "Sample App Area Good"
|
|||
{
|
||||
field("No."; Rec."No.")
|
||||
{
|
||||
ApplicationArea = All;
|
||||
ToolTip = 'Specifies the number that identifies the customer.';
|
||||
}
|
||||
field(Name; Rec.Name)
|
||||
{
|
||||
ApplicationArea = All;
|
||||
ToolTip = 'Specifies the customer''s name.';
|
||||
}
|
||||
}
|
||||
|
|
@ -27,7 +27,6 @@ page 50374 "Sample App Area Good"
|
|||
{
|
||||
action(Refresh)
|
||||
{
|
||||
ApplicationArea = All;
|
||||
ToolTip = 'Reloads the current record.';
|
||||
|
||||
trigger OnAction()
|
||||
|
|
@ -38,3 +37,18 @@ page 50374 "Sample App Area Good"
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pageextension 50376 "Customer App Area Good" extends "Customer Card"
|
||||
{
|
||||
layout
|
||||
{
|
||||
addlast(General)
|
||||
{
|
||||
field("Language Code Sample"; Rec."Language Code")
|
||||
{
|
||||
ApplicationArea = All;
|
||||
ToolTip = 'Specifies the language used for the customer.';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,28 +1,32 @@
|
|||
---
|
||||
bc-version: [all]
|
||||
domain: style
|
||||
keywords: [application-area, page-control, as0062, appsourcecop, hidden-control, web-client]
|
||||
keywords: [application-area, page-control, inheritance, as0062, appsourcecop, web-client]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# Every page control needs an `ApplicationArea` (AppSourceCop AS0062)
|
||||
# Page-level `ApplicationArea` inheritance does not apply to extensions
|
||||
|
||||
## Description
|
||||
|
||||
A field control on a page or pageextension that has no `ApplicationArea` property is silently hidden in the Web client for every profile whose enabled application areas do not cover it. There is no error and no warning at runtime — the field simply does not appear, which reads as data loss to the user. AppSourceCop AS0062 flags any page control or action that is missing the `ApplicationArea` property, and AppSource technical validation rejects the app until it is set.
|
||||
A page control or action needs an effective `ApplicationArea` to appear in cloud experiences. From runtime 10.0, controls on a page object inherit the page-level value, so repeating it on every child is unnecessary when the parent defines a suitable default. This inheritance does not apply to controls added or modified by page and report extensions: extension controls must still set the property explicitly.
|
||||
|
||||
Set the property to an area the app actually enables. `All` makes the control visible under every profile and is the common default; if the app declares narrower areas in `app.json`, use one of those. The property applies to field controls and to actions. This is a sibling concern to `caption-required-on-page-fields.md` and `tooltip-required-on-page-fields.md`; note that the ToolTip requirement is the separate CodeCop rule AA0218, not AS0062.
|
||||
For targets before runtime 10.0, child controls do not inherit and must also set the property. AppSourceCop AS0062 and PTE0008 account for page-level inheritance on runtime 10.0 and later but continue to require explicit values in extensions.
|
||||
|
||||
## Best Practice
|
||||
|
||||
Every field control and action carries `ApplicationArea = All;` (or a declared area of the app). The value is set once per control and keeps the control visible in the Web client.
|
||||
On runtime 10.0 or later, set a suitable page-level default and override only controls that belong to a narrower area. Set `ApplicationArea` explicitly on every control or action introduced by a page or report extension.
|
||||
|
||||
See sample: `applicationarea-required-on-page-controls.good.al`.
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
A field control with no `ApplicationArea`. AS0062 flags it, and the control is invisible in the Web client for any profile that does not already enable a matching area.
|
||||
A page object that defines neither a parent nor child value, or an extension control that assumes it inherits from the base page. The control has no effective application area and can be hidden or rejected by analyzer validation.
|
||||
|
||||
See sample: `applicationarea-required-on-page-controls.bad.al`.
|
||||
|
||||
## Reference
|
||||
|
||||
[Set different control properties](https://learn.microsoft.com/en-us/training/modules/work-with-pages/8-controls)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
tableextension 50622 "Ship-to Dropdown Bad" extends "Ship-to Address"
|
||||
{
|
||||
fieldgroups
|
||||
{
|
||||
addlast(DropDown; "Address 2")
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
tableextension 50620 "Ship-to Dropdown Good" extends "Ship-to Address"
|
||||
{
|
||||
fieldgroups
|
||||
{
|
||||
addlast(DropDown; "Address 2")
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pageextension 50621 "Ship-to Lookup Good" extends "Ship-to Address List"
|
||||
{
|
||||
layout
|
||||
{
|
||||
modify("Address 2")
|
||||
{
|
||||
Visible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
bc-version: [all]
|
||||
domain: ui
|
||||
keywords: [fieldgroup, dropdown, addlast, lookup-page, visible, tableextension, pageextension]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# A `DropDown` field remains hidden when its lookup-page control is hidden
|
||||
|
||||
## Description
|
||||
|
||||
A tableextension can append a field to the `DropDown` field group with `addlast`, but the client still omits that field when its control on the underlying lookup page has `Visible = false`. Changing only the table field group therefore compiles while producing no visible UI change. The field-group name is case-sensitive and must be written as `DropDown`.
|
||||
|
||||
## Best Practice
|
||||
|
||||
When adding a hidden field to a `DropDown` field group, also extend the page used for the lookup and make that field control visible. Verify the actual lookup page rather than assuming the table definition alone controls the drop-down.
|
||||
|
||||
See sample: `dropdown-fieldgroup-respects-lookup-page-visibility.good.al`.
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Adding the field with `addlast(DropDown; ...)` while leaving its lookup-page control hidden, then expecting the field to appear in the drop-down.
|
||||
|
||||
See sample: `dropdown-fieldgroup-respects-lookup-page-visibility.bad.al`.
|
||||
|
||||
## Reference
|
||||
|
||||
[Add a new FieldGroup to an existing table](https://learn.microsoft.com/en-us/training/modules/extend-modify-existing-table/add-field-group)
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
page 50631 "Sample Order Bad"
|
||||
{
|
||||
PageType = Document;
|
||||
SourceTable = "Sales Header";
|
||||
|
||||
layout
|
||||
{
|
||||
area(Content)
|
||||
{
|
||||
group(General)
|
||||
{
|
||||
field(Amount; Rec.Amount)
|
||||
{
|
||||
ApplicationArea = All;
|
||||
ToolTip = 'Specifies the total amount of the order.';
|
||||
}
|
||||
}
|
||||
part(Lines; "Sales Order Subform")
|
||||
{
|
||||
ApplicationArea = All;
|
||||
SubPageLink = "Document Type" = field("Document Type"),
|
||||
"Document No." = field("No.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
page 50630 "Sample Order Good"
|
||||
{
|
||||
PageType = Document;
|
||||
SourceTable = "Sales Header";
|
||||
|
||||
layout
|
||||
{
|
||||
area(Content)
|
||||
{
|
||||
group(General)
|
||||
{
|
||||
field(Amount; Rec.Amount)
|
||||
{
|
||||
ApplicationArea = All;
|
||||
ToolTip = 'Specifies the total amount of the order.';
|
||||
}
|
||||
}
|
||||
part(Lines; "Sales Order Subform")
|
||||
{
|
||||
ApplicationArea = All;
|
||||
SubPageLink = "Document Type" = field("Document Type"),
|
||||
"Document No." = field("No.");
|
||||
UpdatePropagation = Both;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
bc-version: [all]
|
||||
domain: ui
|
||||
keywords: [updatepropagation, page-part, subpage, main-page, refresh, flowfield, document-lines]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# Use `UpdatePropagation = Both` when line edits must refresh the main page
|
||||
|
||||
## Description
|
||||
|
||||
A page part does not automatically refresh its parent page when the subpage changes. `UpdatePropagation = Subpage` updates only the part; `Both` also refreshes the main page. Without `Both`, header totals, FlowFields, and FactBoxes that depend on edited lines can remain stale until another user action refreshes the page.
|
||||
|
||||
## Best Practice
|
||||
|
||||
Set `UpdatePropagation = Both` on a part when edits in that subpage must immediately update values rendered by the main page. Leave propagation at `Subpage` when the parent has no dependent presentation to avoid unnecessary refreshes.
|
||||
|
||||
See sample: `updatepropagation-both-refreshes-main-page.good.al`.
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Displaying a line-dependent total on the main page while the editable lines part updates only itself. The persisted values can be correct while the parent page continues to show an old total.
|
||||
|
||||
See sample: `updatepropagation-both-refreshes-main-page.bad.al`.
|
||||
|
||||
## Reference
|
||||
|
||||
[Set different control properties](https://learn.microsoft.com/en-us/training/modules/work-with-pages/8-controls)
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
---
|
||||
bc-version: [all]
|
||||
domain: upgrade
|
||||
keywords: [appversion, dataversion, moduleinfo, install-codeunit, upgrade-codeunit, version-context]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# `ModuleInfo.AppVersion` changes meaning with execution context
|
||||
|
||||
## Description
|
||||
|
||||
`ModuleInfo.AppVersion()` is the installed version during normal operation, the version being installed inside install code, and the target version inside upgrade code. It is therefore not the source data version during an upgrade. In upgrade code, `DataVersion()` describes the version of the existing data, whether from the currently installed app or the version most recently uninstalled.
|
||||
|
||||
## Best Practice
|
||||
|
||||
Interpret `AppVersion()` as the code package entering the context and `DataVersion()` as the existing data state. Prefer upgrade tags for controlling individual migration steps; when version information is needed for diagnostics or preconditions, name variables so target app version and source data version cannot be confused.
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Reading `AppVersion()` from an upgrade codeunit and treating it as the version being upgraded from. The comparison actually observes the target package and can skip or misroute migration logic.
|
||||
|
||||
## Reference
|
||||
|
||||
[Create proper installation and upgrade codeunits](https://learn.microsoft.com/en-us/training/modules/easy-application-upgrade/3-installation-upgrade-codeunits)
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
codeunit 50641 "Sample Upgrade Part One"
|
||||
{
|
||||
Subtype = Upgrade;
|
||||
|
||||
trigger OnUpgradePerCompany()
|
||||
begin
|
||||
CreateUpgradeState();
|
||||
end;
|
||||
|
||||
local procedure CreateUpgradeState()
|
||||
begin
|
||||
end;
|
||||
}
|
||||
|
||||
codeunit 50642 "Sample Upgrade Part Two"
|
||||
{
|
||||
Subtype = Upgrade;
|
||||
|
||||
trigger OnUpgradePerCompany()
|
||||
begin
|
||||
// This can run before Part One; object IDs do not sequence upgrade codeunits.
|
||||
MigrateDataThatRequiresUpgradeState();
|
||||
end;
|
||||
|
||||
local procedure MigrateDataThatRequiresUpgradeState()
|
||||
begin
|
||||
end;
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
codeunit 50640 "Sample Upgrade Good"
|
||||
{
|
||||
Subtype = Upgrade;
|
||||
|
||||
trigger OnUpgradePerCompany()
|
||||
begin
|
||||
CreateUpgradeState();
|
||||
MigrateDependentData();
|
||||
end;
|
||||
|
||||
local procedure CreateUpgradeState()
|
||||
begin
|
||||
end;
|
||||
|
||||
local procedure MigrateDependentData()
|
||||
begin
|
||||
end;
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
---
|
||||
bc-version: [all]
|
||||
domain: upgrade
|
||||
keywords: [install-codeunit, upgrade-codeunit, execution-order, subtype-install, subtype-upgrade, sequencing]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# Separate install or upgrade codeunits have no execution order
|
||||
|
||||
## Description
|
||||
|
||||
An extension can contain multiple `Install` or `Upgrade` codeunits, but Business Central does not guarantee the order in which codeunits of the same subtype execute. Upgrade trigger phases are ordered globally, yet one codeunit's `OnUpgradePerCompany` must not assume another codeunit's same-phase trigger already ran. Object ID and source-file order do not provide sequencing.
|
||||
|
||||
## Best Practice
|
||||
|
||||
Keep separate install or upgrade codeunits independent. When two steps have a real dependency, coordinate them from one owning trigger in the required order; use upgrade tags to make each completed step idempotent.
|
||||
|
||||
See sample: `install-and-upgrade-codeunits-have-no-order.good.al`.
|
||||
|
||||
## Anti Pattern
|
||||
|
||||
Splitting dependent steps into separate codeunits and relying on names, object IDs, or declaration order. The dependent codeunit can run first and fail or observe partially migrated data.
|
||||
|
||||
See sample: `install-and-upgrade-codeunits-have-no-order.bad.al`.
|
||||
|
||||
## Reference
|
||||
|
||||
[Create proper installation and upgrade codeunits](https://learn.microsoft.com/en-us/training/modules/easy-application-upgrade/3-installation-upgrade-codeunits)
|
||||
68
microsoft/skills/development/al-development-plan.md
Normal file
68
microsoft/skills/development/al-development-plan.md
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
---
|
||||
kind: action-skill
|
||||
id: al-development-plan
|
||||
version: 1
|
||||
title: AL development plan guidance
|
||||
description: Produces a read-only BCQuality knowledge bundle for an existing Business Central AL development plan.
|
||||
inputs: [development-plan, repository]
|
||||
outputs: [development-guidance-report]
|
||||
bc-version: [all]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [all]
|
||||
---
|
||||
|
||||
# AL development plan guidance
|
||||
|
||||
Selects the BCQuality knowledge that should constrain an existing AL development plan. It does not implement, edit, stage, commit, or publish anything in the target repository. Repository-specific orchestrators can consume this skill before their own test and implementation phases while retaining ownership of workflow, tooling, and delivery.
|
||||
|
||||
Both a readable `repository` and a non-empty `development-plan` are required. The plan may be structured data or text, but it must identify the intended change. Return `not-applicable` without changing files when either input is absent or the repository is not an AL project.
|
||||
|
||||
## Source
|
||||
|
||||
Read the BCQuality knowledge index once. Use entries from every enabled layer and domain. The index supplies candidate paths, applicability dimensions, keywords, titles, and descriptions; it never substitutes for opening selected articles in full.
|
||||
|
||||
Inspect the target repository read-only for `app.json`, affected files and symbols named by the plan, relevant tests, permission sets, dependencies, target/runtime versions, countries, application areas, and repository conventions. Do not create scratch or generated files inside the target repository.
|
||||
|
||||
## Relevance
|
||||
|
||||
Apply READ's matching semantics using:
|
||||
|
||||
- `bc-version` from the plan, target application, or supplied context; for upgrades, distinguish source and target versions.
|
||||
- `technologies` from the affected files, beginning with `[al]`.
|
||||
- `countries` from the plan, `app.json`, or workspace configuration.
|
||||
- `application-area` from the plan and affected objects.
|
||||
|
||||
When a dimension cannot be resolved, retain conditionally applicable candidates only when they can materially constrain the plan. Record the dimension in `context.unknown` and explain it in `unresolved`; do not silently treat it as a match.
|
||||
|
||||
## Worklist
|
||||
|
||||
1. Normalize the plan into: request summary, development kind, assumptions, root cause or design intent, affected files and symbols, proposed changes, test strategy, and acceptance criteria. When the plan has no normalized kind, apply the same categories as `al-development`: new or expanded behavior is `feature`, a defect correction is `bug`, behavior-preserving restructuring is `refactor`, migration is `upgrade`, and other bounded work is `maintenance`. A repository-specific additive event or extensibility request maps to `feature`; retain its original work-item type in the request summary. Do not redesign the repository-specific workflow.
|
||||
2. Build retrieval vocabulary from the plan and confirmed repository symbols. Give exact object types, properties, methods, analyzers, errors, and affected domains more weight than broad business nouns.
|
||||
3. Search the index in separate passes:
|
||||
- data ownership, keys, setup, numbering, validation, transactions, and upgrade;
|
||||
- behavior, events, interfaces, errors, permissions, privacy, and telemetry;
|
||||
- pages, reports, APIs, integrations, localization, and accessibility;
|
||||
- tests, analyzers, packaging, and deployment constraints.
|
||||
4. Add an article when its keywords or indexed topic match a concrete planned change, affected symbol, acceptance criterion, or validation obligation. Applicability alone is not enough.
|
||||
5. Open every selected article in full. Read any referenced `.good.*` and `.bad.*` sibling needed to make the constraint concrete. Never cite an index row that was not opened.
|
||||
6. Resolve contradictory normative guidance with READ's layer precedence and record losing candidates in `suppressed`.
|
||||
7. Check the resulting worklist across the whole plan. A bug fix may require testing, data, performance, and upgrade guidance at once; a feature plan may require security and lifecycle constraints that are not named in its title.
|
||||
|
||||
Keep the worklist focused. Do not include generic engineering advice, an entire domain, or an article that would not change implementation or validation.
|
||||
|
||||
## Action
|
||||
|
||||
For each worklist article:
|
||||
|
||||
1. Copy its exact path and optional commit SHA.
|
||||
2. State `used-for` as the concrete plan decision or affected surface.
|
||||
3. Translate its normative Best Practice and Anti Pattern into short implementation constraints without adding facts or weakening conditions.
|
||||
4. Include only opened, existing sibling samples in `sample-paths`.
|
||||
5. Derive validation considerations only where the plan or selected knowledge requires observable evidence. Describe the evidence to obtain; do not claim it already exists or passed.
|
||||
|
||||
Do not change the target repository. Before emitting, verify every knowledge and sample path exists in the live BCQuality checkout and was opened during this run. If reference integrity cannot be established, return `failed` rather than fabricating guidance.
|
||||
|
||||
## Output
|
||||
|
||||
Return one `development-guidance-report` conforming to DO. `completed` requires that every selected article was opened and faithfully converted into constraints. `no-knowledge` is valid when the plan is applicable but BCQuality contains no relevant article. `partial` names every unevaluated candidate or unresolved applicability gap.
|
||||
91
microsoft/skills/development/al-development.md
Normal file
91
microsoft/skills/development/al-development.md
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
---
|
||||
kind: action-skill
|
||||
id: al-development
|
||||
version: 1
|
||||
title: AL development
|
||||
description: Implements Business Central AL features, bug fixes, refactors, upgrades, and maintenance changes using BCQuality knowledge.
|
||||
inputs: [development-request, repository]
|
||||
outputs: [implementation-report]
|
||||
bc-version: [all]
|
||||
technologies: [al]
|
||||
countries: [w1]
|
||||
application-area: [all]
|
||||
guidance-skill: microsoft/skills/development/al-development-plan.md
|
||||
quality-skill: microsoft/skills/review/al-code-review.md
|
||||
---
|
||||
|
||||
# AL development
|
||||
|
||||
Implements a Business Central change in an existing AL repository. Feature work, bug fixing, refactoring, upgrades, and maintenance share one public contract and one quality pipeline; their different investigation disciplines are execution modes within this skill.
|
||||
|
||||
Both a writable `repository` and a `development-request` are required. A structured request has this shape:
|
||||
|
||||
```yaml
|
||||
development-request:
|
||||
kind: auto # feature | bug | refactor | upgrade | maintenance
|
||||
description: string # optional when plan states the requested outcome
|
||||
plan: string # optional
|
||||
acceptance-criteria: [string] # optional
|
||||
```
|
||||
|
||||
A plain-text request is normalized to `kind: auto` with the text as `description`. A plan-only request is valid when the plan states the requested outcome. Return `not-applicable` without changing files when either input is absent, both description and plan are empty, or the repository is not an AL project.
|
||||
|
||||
## Source
|
||||
|
||||
Read the frontmatter `guidance-skill`; it owns BCQuality discovery and returns the knowledge constraints for the implementation plan. Inspect the target repository for `app.json`, existing objects, tests, permission sets, analyzers, build scripts, naming and object-ID conventions, dependencies, target/runtime versions, localization layout, and uncommitted user changes. For bugs, refactors, and upgrades, inspect enough history and surrounding code to establish the behavior being changed.
|
||||
|
||||
## Relevance
|
||||
|
||||
Resolve and pass this context to the guidance-skill:
|
||||
|
||||
- `bc-version` from the target application's platform/application/runtime settings or supplied context. For an upgrade, distinguish source and target versions.
|
||||
- `technologies: [al]`, plus any additional technology actually required by the request.
|
||||
- `countries` from `app.json`, workspace configuration, or supplied context.
|
||||
- `application-area` from the request and affected objects.
|
||||
|
||||
Record unresolved dimensions in the development plan rather than silently substituting broad values. The guidance-skill applies READ's matching semantics and returns any conditional applicability in its report.
|
||||
|
||||
## Worklist
|
||||
|
||||
1. Normalize the request, deriving a concise description from a plan-only input, and classify `kind: auto` as:
|
||||
- `feature` for new or intentionally expanded behavior;
|
||||
- `bug` for observed behavior that contradicts an expected result;
|
||||
- `refactor` for structural change with no intended behavior change;
|
||||
- `upgrade` for schema, data, dependency, runtime, or application-version migration;
|
||||
- `maintenance` for bounded development work that fits none of the above.
|
||||
Preserve an explicit valid kind. When repository evidence conflicts with it, record the mismatch and ask for clarification before changing files rather than silently switching disciplines.
|
||||
2. Establish the mode-specific implementation contract:
|
||||
- **Feature:** define user-visible behavior and cover data lifecycle, UI/API, permissions, extensibility, upgrade impact, telemetry, and tests where applicable.
|
||||
- **Bug:** state expected versus actual behavior, reproduce or otherwise prove the defect, trace the root cause, and define a regression test that fails for that cause.
|
||||
- **Refactor:** identify the behavior and public contracts that must remain invariant, plus the checks that establish a before/after baseline.
|
||||
- **Upgrade:** identify source and target states, data migration, compatibility, idempotency, and validation requirements.
|
||||
- **Maintenance:** define the bounded outcome and the behavior that must not change.
|
||||
3. Treat a supplied plan as an input constraint, not as proof. Reconcile it with repository reality and BCQuality; preserve its intent, correct unsafe assumptions, and record consequential deviations.
|
||||
4. Discover existing implementation patterns and reusable objects before proposing new ones. Preserve repository conventions and current user changes.
|
||||
5. Materialize a `development-plan` containing the classified kind, request, assumptions, affected files and symbols, design or root cause, proposed changes, validation strategy, and acceptance criteria.
|
||||
6. Invoke the frontmatter `guidance-skill` with that plan, the repository, and the resolved context. It performs Source, Relevance, and knowledge worklisting independently and read-only.
|
||||
7. Require a complete guidance result before editing product code:
|
||||
- `completed` — use every returned constraint and validation consideration.
|
||||
- `no-knowledge` — return `no-knowledge` without implementing a Business Central-specific change.
|
||||
- `not-applicable`, `partial`, or `failed` — return the corresponding non-completed outcome without editing product code; preserve its reason in `remaining`.
|
||||
8. Copy the guidance report's selected paths into the eventual implementation report only when the corresponding constraint materially shaped the implementation. Carry its suppression records forward.
|
||||
|
||||
## Action
|
||||
|
||||
1. Record the starting working-tree state so unrelated changes are preserved and excluded from `changes`.
|
||||
2. Apply the execution mode:
|
||||
- **Feature:** implement the smallest complete vertical slice; do not leave placeholder surfaces.
|
||||
- **Bug:** reproduce first when feasible, fix the root cause rather than the symptom, keep the patch surgical, and add a regression test.
|
||||
- **Refactor:** capture a behavioral baseline, avoid unrelated behavior changes, and prove the declared invariants afterward.
|
||||
- **Upgrade:** make migrations rerunnable where required, preserve data and compatibility, and validate both upgraded and fresh-install paths when applicable.
|
||||
- **Maintenance:** make only the bounded requested change and preserve surrounding behavior.
|
||||
3. Produce a coherent design that satisfies the implementation contract and every constraint returned by the guidance-skill. Reuse existing abstractions and object ranges. Do not hard-code a Business Central fact in this skill or invent a rule absent from both the repository and reliable platform knowledge.
|
||||
4. Implement the request end to end. Include all surfaces required by the mode, acceptance criteria, and repository conventions. Do not create success-shaped stubs.
|
||||
5. Treat the guidance report as design constraints throughout implementation. Adapt its referenced companion samples to the target codebase; never copy demonstration IDs or names blindly.
|
||||
6. Run the smallest existing build, analyzer, and test commands that cover the change. Fix failures caused by the implementation. Record every command and real outcome in `validation`; unavailable checks are `not-run`, never `passed`.
|
||||
7. Invoke the frontmatter `quality-skill` against the final implementation diff. Fix all justified knowledge-backed `blocker` and `major` findings and concrete defects introduced by this work, then rerun affected validation and review. Preserve the last findings-report in `review` and add a `validation` entry with `id: "review"`. If review is disabled or unavailable, record `not-run` and return `partial`.
|
||||
8. Verify the persisted files against the implementation contract, acceptance criteria, and mode-specific evidence. If behavior, validation, or review remains incomplete, return `partial` and list the exact gap in `remaining`.
|
||||
|
||||
## Output
|
||||
|
||||
Return one `implementation-report` conforming to DO. Set `plan.kind` to the classified execution mode. `knowledge` lists only articles opened in full and materially used. `changes` lists only files changed by this skill. `completed` requires a persisted implementation, passing required validation, and no unresolved `blocker` or `major` finding in `review`.
|
||||
|
|
@ -39,7 +39,7 @@ Narrow the relevant files to the subset that applies to the changes under review
|
|||
|
||||
- The changed AL object names and types — especially `* Setup` singleton tables and Card pages, custom master tables, tableextensions that add master-data fields, and document or journal lines that reference a master.
|
||||
- The changed fields, keys, triggers, and procedures, weighted toward `Primary Key`, `No.`, `No. Series`, `Blocked`, `Last Date Modified`, `OnInsert`, `OnModify`, `OnRename`, reference-field `OnValidate`, and posting validation.
|
||||
- Tokens extracted from the diff that relate to data modeling (`setup`, `master`, `Primary Key`, `Code[10]`, `Code[20]`, `AutoIncrement`, `SystemId`, `No.`, `No. Series`, `NoSeriesManagement`, `Codeunit "No. Series"`, `GetNextNo`, `IsManual`, `TestManual`, `Blocked`, `TestField`, `Last Date Modified`, `Today`, `WorkDate`, `InsertAllowed`, `DeleteAllowed`, `PageType = Card`, `OnOpenPage`, `GetRecordOnce`, `OnInsert`, `OnModify`, `OnRename`, `TableRelation`, `tableextension`, `enumextension`, `Media`, `MediaSet`, `Item`, `Count`).
|
||||
- Tokens extracted from the diff that relate to data modeling (`setup`, `master`, `Primary Key`, `Code[10]`, `Code[20]`, `AutoIncrement`, `SystemId`, `No.`, `No. Series`, `NoSeriesManagement`, `Codeunit "No. Series"`, `GetNextNo`, `IsManual`, `TestManual`, `Blocked`, `TestField`, `Last Date Modified`, `Today`, `WorkDate`, `InsertAllowed`, `DeleteAllowed`, `PageType = Card`, `OnOpenPage`, `GetRecordOnce`, `OnInsert`, `OnModify`, `OnRename`, `InitRecord`, `Round`, `Precision`, `Direction`, `TableRelation`, `tableextension`, `enumextension`, `Media`, `MediaSet`, `Item`, `Count`).
|
||||
|
||||
A file enters the candidate worklist when its `keywords` intersect the extracted tokens or its topic (derived from the index entry's `path`, `title`, and `description`) matches a changed object type. Read an article's full file — its `## Best Practice` / `## Anti Pattern` bodies — only after it makes the worklist; candidate selection uses the index alone. When the diff contains no data-modeling changes by any of the above signals, return `outcome: "not-applicable"` without evaluating files.
|
||||
|
||||
|
|
@ -52,6 +52,8 @@ The following targeted checks cover every current `data-modeling` article. Treat
|
|||
- A master table adds or changes `Last Date Modified`, `OnModify`, or `OnRename`, but the non-editable field is not assigned `Today()` in both triggers — `set-last-date-modified-in-onmodify-and-onrename`.
|
||||
- A `tableextension` appends a conditional `TableRelation` as if it overrides an earlier unconditional relation, or relation branches are otherwise designed without accounting for additive top-down evaluation — `table-relation-extensions-are-additive-and-top-down`.
|
||||
- A `Media` or `MediaSet` field is assigned directly between different table types or different field IDs instead of registering each shared item with `MediaSet.Insert` — `share-mediaset-items-with-insert-not-field-assignment`.
|
||||
- A custom document header assigns defaults outside an `InitRecord` boundary, calls `InitRecord` before assigning its number, or places UI-independent defaults only in a page trigger — `initialize-document-defaults-in-initrecord`.
|
||||
- Directed `Round` calls use `'<'` as mathematical floor or `'>'` as mathematical ceiling, especially where negative amounts are possible — `round-direction-symbols-use-magnitude`.
|
||||
|
||||
Once the candidate worklist is known, resolve layer-precedence conflicts per READ. Drop lower-precedence files whose normative guidance (`## Best Practice` or `## Anti Pattern`) directly contradicts a higher-precedence candidate, and record each dropped file in `suppressed` with `reason: "layer-precedence"`. Files that would have been candidates but are hidden because their layer is disabled in consumer configuration are recorded with `reason: "configuration"`. Files that never became candidates are NOT recorded in `suppressed`.
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ Narrow the relevant files to the subset that applies to the changes under review
|
|||
|
||||
- The changed AL object names and types — especially `interface` objects, codeunits and enums declared with the `implements` keyword, and consumers that declare or assign an `Interface` variable.
|
||||
- The changed procedures and triggers, weighted toward factory or dispatch routines that resolve a variant to behaviour, setter-injection procedures that take an `Interface` parameter, and `case`-over-enum blocks that select between strategies.
|
||||
- Tokens extracted from the diff that relate to interfaces and enum-backed implementation (`interface`, `extends`, `implements`, `Implementation`, `DefaultImplementation`, `UnknownValueImplementation`, `enum`, `Extensible`, `Interface`, `case`, and the `case <enum> of` anti-pattern signal — a `case` over an enum value whose branches choose between variant computations).
|
||||
- Tokens extracted from the diff that relate to interfaces and enum-backed implementation (`interface`, `extends`, `implements`, `Implementation`, `DefaultImplementation`, `UnknownValueImplementation`, `enum`, `Extensible`, `Interface`, `Variant`, `is`, `as`, `case`, and the `case <enum> of` anti-pattern signal — a `case` over an enum value whose branches choose between variant computations).
|
||||
|
||||
A file enters the candidate worklist when its `keywords` intersect the extracted tokens or its topic (derived from the index entry's `path`, `title`, and `description`) matches a changed object type. Read an article's full file — its `## Best Practice` / `## Anti Pattern` bodies — only after it makes the worklist; candidate selection uses the index alone.
|
||||
|
||||
|
|
@ -54,6 +54,7 @@ The following targeted checks map diff signals to specific `interfaces` articles
|
|||
- `DefaultImplementation` used as the only fallback where a persisted ordinal may no longer match any declared enum value, or a persisted enum lacks `UnknownValueImplementation` on BC18 or later — `handle-unknown-enum-ordinals-with-unknownvalueimplementation`.
|
||||
- A method added directly to an interface that exists in the baseline, instead of adding a BC25+ interface that `extends` it or a versioned sibling for older targets — `extend-published-interfaces-dont-edit-them`.
|
||||
- A declared enum value with no `Implementation` and no enum-level `DefaultImplementation` — `set-defaultimplementation-on-enum`.
|
||||
- An `Interface` or `Variant` is cast with `as` to an optional extended interface without first establishing support with `is` — `guard-interface-casts-with-is`.
|
||||
|
||||
For `set-defaultimplementation-on-enum`, inspect the complete containing enum before emitting. An enum-level `DefaultImplementation = <Interface> = <Codeunit>;` conclusively covers every declared value that omits its own `Implementation`; do not flag such a value and do not replace the intentional fallback with a per-value mapping.
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ Narrow the relevant files to the subset that applies to the changes under review
|
|||
|
||||
- Changed AL objects — especially API pages (`PageType = API`), tables and pages declaring Labels/TextConsts, codeunits issuing `Error`/`Message`/`Confirm`, and any file whose name violates the `<ObjectName>.<ObjectType>.al` convention.
|
||||
- Changed declarations, weighted toward `: Label '...'`, `: TextConst '...'`, temporary record variables, option fields, error-handling call sites, and codeunit-internal method calls.
|
||||
- Tokens extracted from the diff (`Label`, `TextConst`, `Locked`, `Comment`, `MaxLength`, `temporary`, `OptionMembers`, `OptionCaption`, `APIPublisher`, `APIGroup`, `APIVersion`, `EntityName`, `EntitySetName`, `DelayedInsert`, `FieldCaption`, `TableCaption`, `FieldName`, `TableName`, `Page.RunModal`, `Report.Run`, `this.`, `StrSubstNo`).
|
||||
- Tokens extracted from the diff (`Label`, `TextConst`, `Locked`, `Comment`, `MaxLength`, `temporary`, `OptionMembers`, `OptionCaption`, `ApplicationArea`, `APIPublisher`, `APIGroup`, `APIVersion`, `EntityName`, `EntitySetName`, `DelayedInsert`, `FieldCaption`, `TableCaption`, `FieldName`, `TableName`, `Page.RunModal`, `Report.Run`, `this.`, `StrSubstNo`).
|
||||
|
||||
A file enters the candidate worklist when its `keywords` intersect the extracted tokens or its topic (derived from the index entry's `path`, `title`, and `description`) matches a changed object or declaration. Read an article's full file — its `## Best Practice` / `## Anti Pattern` bodies — only after it makes the worklist; candidate selection uses the index alone.
|
||||
|
||||
|
|
@ -51,6 +51,7 @@ Apply these high-signal mappings before fuzzy topic ranking:
|
|||
|
||||
- A `Label` or `TextConst` contains multiple or ambiguous placeholders but has no `Comment`, or its Comment does not explain every placeholder — `label-comment-explains-placeholders.md`. A single placeholder whose meaning is explicit in the text, such as `Customer %1`, is allowed without a Comment and must not be flagged.
|
||||
- `function-call-parentheses-required.md` applies only to a zero-argument invocation written without `()`. Never worklist it from an invocation that already has parentheses or supplies arguments, including `Error(Label, Arg1, Arg2)`.
|
||||
- On runtime 10.0 or later, a page child may inherit `ApplicationArea` from its page object; do not flag that shape. A control added by a page or report extension still requires an explicit value — `applicationarea-required-on-page-controls.md`.
|
||||
|
||||
Once the candidate worklist is known, resolve layer-precedence conflicts per READ and record suppressions.
|
||||
|
||||
|
|
|
|||
|
|
@ -41,10 +41,15 @@ Narrow the relevant files to the subset that applies to the changes under review
|
|||
|
||||
- **UI-file filter.** UI review applies to files declaring `page`, `pageextension`, or `pagecustomization`, and to JavaScript/CSS/HTML that implements a control add-in's rendering or Business Central communication. When the diff contains no such files, return `outcome: "not-applicable"` without evaluating knowledge files.
|
||||
- For each relevant knowledge file, compute overlap against changed page declarations and control add-in files, weighted toward `Caption`, `ToolTip`, `AboutTitle`, `AboutText`, `OptionCaption`, `ShowCaption`, `InstructionalText`, `GridLayout`, `Style`, `StyleExpr`, promoted action definitions, field importance, page background tasks, DOM creation, ARIA attributes, keyboard/focus handlers, packaged-resource AJAX, and calls from JavaScript into AL.
|
||||
- Tokens extracted from the diff (`Caption`, `ToolTip`, `AboutTitle`, `AboutText`, `PageType`, `ShowCaption`, `InstructionalText`, `grid`, `fixed`, `GridLayout`, `Style`, `StyleExpr`, `Importance`, `Promoted`, `Additional`, `area(Promoted)`, `actionref`, `PromotedCategory`, `PromotedOnly`, `PromotedIsBig`, `ShowAs`, `SplitButton`, `EnqueueBackgroundTask`, `OnAfterGetCurrRecord`, `OnAfterGetRecord`, `OnPageBackgroundTaskCompleted`, `OnPageBackgroundTaskError`, `RunPageBackgroundTask`, `Favorable`, `Unfavorable`, `Ambiguous`, `cuegroup`, `controladdin`, `control-add-in`, `usercontrol`, `aria-`, `tabindex`, `keydown`, `focus`, `innerHTML`, `createElement`, `packaged-resource`, `ajax`, `$.get`, `$.ajax`, `XMLHttpRequest`, `xhrFields`, `withCredentials`, `withcredentials`, `InvokeExtensibilityMethod`, `invokeextensibilitymethod`, `skipIfBusy`, `successCallback`, `success-callback`, `errorCallback`, `setInterval`, `JSON.stringify`, `payload`, `throttling`, `reduced-functionality`, `ClientServicesMaxUploadSize`, `&`, `Specifies`, `Message(`, `Confirm(`, `Error(` in a page context, `Disabled`, `Invalid`, `Whitelist`, `Blacklist`, trailing punctuation patterns on captions).
|
||||
- Tokens extracted from the diff (`Caption`, `ToolTip`, `AboutTitle`, `AboutText`, `PageType`, `ShowCaption`, `InstructionalText`, `grid`, `fixed`, `GridLayout`, `Style`, `StyleExpr`, `Importance`, `Promoted`, `Additional`, `area(Promoted)`, `actionref`, `PromotedCategory`, `PromotedOnly`, `PromotedIsBig`, `ShowAs`, `SplitButton`, `fieldgroups`, `DropDown`, `UpdatePropagation`, `EnqueueBackgroundTask`, `OnAfterGetCurrRecord`, `OnAfterGetRecord`, `OnPageBackgroundTaskCompleted`, `OnPageBackgroundTaskError`, `RunPageBackgroundTask`, `Favorable`, `Unfavorable`, `Ambiguous`, `cuegroup`, `controladdin`, `control-add-in`, `usercontrol`, `aria-`, `tabindex`, `keydown`, `focus`, `innerHTML`, `createElement`, `packaged-resource`, `ajax`, `$.get`, `$.ajax`, `XMLHttpRequest`, `xhrFields`, `withCredentials`, `withcredentials`, `InvokeExtensibilityMethod`, `invokeextensibilitymethod`, `skipIfBusy`, `successCallback`, `success-callback`, `errorCallback`, `setInterval`, `JSON.stringify`, `payload`, `throttling`, `reduced-functionality`, `ClientServicesMaxUploadSize`, `&`, `Specifies`, `Message(`, `Confirm(`, `Error(` in a page context, `Disabled`, `Invalid`, `Whitelist`, `Blacklist`, trailing punctuation patterns on captions).
|
||||
|
||||
A file enters the candidate worklist when its `keywords` intersect the extracted tokens or its topic (derived from the index entry's `path`, `title`, and `description`) matches a changed page element. Read an article's full file — its `## Best Practice` / `## Anti Pattern` bodies — only after it makes the worklist; candidate selection uses the index alone.
|
||||
|
||||
Apply these high-signal mappings before fuzzy topic ranking:
|
||||
|
||||
- A tableextension adds a field to `DropDown` while the corresponding lookup-page control remains `Visible = false` — `dropdown-fieldgroup-respects-lookup-page-visibility`.
|
||||
- An editable page part affects a total, FlowField, or FactBox on the parent but does not set `UpdatePropagation = Both` — `updatepropagation-both-refreshes-main-page`.
|
||||
|
||||
Once the candidate worklist is known, resolve layer-precedence conflicts per READ and record suppressions.
|
||||
|
||||
When the post-conflict worklist is empty because no applicable UI knowledge exists, or because configuration suppressed every candidate, emit `outcome: "no-knowledge"`. When the worklist is empty because no applicable UI knowledge matched the page changes, emit `outcome: "completed"` with an empty `findings` array.
|
||||
|
|
|
|||
|
|
@ -39,10 +39,12 @@ Narrow the relevant files to the subset that applies to the changes under review
|
|||
|
||||
- The changed AL object names and types — especially codeunits with `Subtype = Upgrade` or `Subtype = Install`, tables and tableextensions adding or changing fields, enums and enumextensions, and objects under `Hybrid*`/`Migration`/`Upgrade` namespaces.
|
||||
- The changed triggers and procedures, weighted toward `OnCheckPreconditionsPerCompany`/`PerDatabase`, `OnUpgradePerCompany`/`PerDatabase`, `OnValidateUpgradePerCompany`/`PerDatabase`, `OnInstallAppPerCompany`/`PerDatabase`, the `OnGetPerCompanyUpgradeTags`/`OnGetPerDatabaseUpgradeTags` subscribers, and helper procedures transitively reachable from those entry points.
|
||||
- Tokens extracted from the diff that relate to upgrade concerns (`Subtype = Upgrade`, `Subtype = Install`, `Upgrade Tag`, `HasUpgradeTag`, `SetUpgradeTag`, `OnCheckPreconditions`, `OnUpgrade`, `OnValidateUpgrade`, `OnInstallApp`, `DataTransfer`, `CopyFields`, `Insert`, `Modify`, `Delete`, `Rename`, `InitValue`, `ObsoleteState`, `ObsoleteReason`, `ObsoleteTag`, `DataVersion`, `ExecutionContext`, `PrimaryKey`, `key(`, `field(`, `value(`, `enum`, `enumextension`, `HybridSL`, `HybridGP`, `HybridBC`, `HybridBaseDeployment`).
|
||||
- Tokens extracted from the diff that relate to upgrade concerns (`Subtype = Upgrade`, `Subtype = Install`, `Upgrade Tag`, `HasUpgradeTag`, `SetUpgradeTag`, `OnCheckPreconditions`, `OnUpgrade`, `OnValidateUpgrade`, `OnInstallApp`, `DataTransfer`, `CopyFields`, `Insert`, `Modify`, `Delete`, `Rename`, `InitValue`, `ObsoleteState`, `ObsoleteReason`, `ObsoleteTag`, `ModuleInfo`, `AppVersion`, `DataVersion`, `NavApp.GetCurrentModuleInfo`, `ExecutionContext`, `PrimaryKey`, `key(`, `field(`, `value(`, `enum`, `enumextension`, `HybridSL`, `HybridGP`, `HybridBC`, `HybridBaseDeployment`).
|
||||
- For each `OnCheckPreconditions...` and `OnValidateUpgrade...` trigger, build the best available call graph from surrounding unchanged source as well as changed hunks, tracing resolved calls through reachable local or internal helpers. Worklist the check-only rule when a database write occurs either directly in the trigger or in any helper procedure reachable from it. Writes include `Insert`, `Modify`, `ModifyAll`, `Delete`, `DeleteAll`, `Rename`, and `DataTransfer`. Also perform the reverse check when a PR changes a writing helper body: worklist the rule when that helper is invoked directly or transitively by an unchanged check or validation trigger.
|
||||
- Treat a direct write or a fully resolved call chain as high-confidence evidence. When cross-object dispatch, unavailable declarations, or an incomplete call graph prevents proving the complete chain, cap confidence at `medium`, name the unresolved edge in the finding, and do not claim a violation without a resolved path from a check or validation trigger to a write.
|
||||
- Worklist the install-versus-upgrade rule when migration helpers are reachable only from an install codeunit.
|
||||
- Worklist `install-and-upgrade-codeunits-have-no-order.md` when a change adds multiple install or upgrade codeunits whose same-phase triggers share state or depend on one another.
|
||||
- Worklist `appversion-meaning-depends-on-execution-context.md` when install or upgrade code branches on `ModuleInfo.AppVersion()` or confuses it with `DataVersion()`.
|
||||
|
||||
A file enters the candidate worklist when its `keywords` intersect the extracted tokens or its topic (derived from the index entry's `path`, `title`, and `description`) matches a changed object type. Read an article's full file — its `## Best Practice` / `## Anti Pattern` bodies — only after it makes the worklist; candidate selection uses the index alone. When the diff contains no upgrade-related changes by any of the above signals, return `outcome: "not-applicable"` without evaluating files.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue