Custom form fields
Custom form fields allow you to add custom fields to Shop Floor Production's registration screens via a per-tenant extension (PTE). This lets customers and partners define fields that users must populate during the production registration process, entirely in AL.
Supported screens
Custom form fields can be added to the following screens:
- Register Input
- Register Output
Read-only custom fields can also be shown on the following correction screens:
- Correct Consumption
- Correct Output
Registering custom form elements
Custom form elements are registered through one of two events:
SFPBCGetFormElements14FDW.Codeunit.al
[IntegrationEvent(false, false)]
local procedure OnAddCustomFormElementsForConsumption(var TempCustomFormElements: Record TempCustomFormElements14FDW temporary; var CustomFormFields: Dictionary of [Text, Text]; ProdOrderComponent: Record "Prod. Order Component")
begin
end;
[IntegrationEvent(false, false)]
local procedure OnAddCustomFormElementsForOutput(var TempCustomFormElements: Record TempCustomFormElements14FDW temporary; ProdOrderRoutingLine: Record "Prod. Order Routing Line")
begin
end;
The CustomFormFields parameter on OnAddCustomFormElementsForConsumption carries any values already pre-populated by custom consumption scan logic. If a value is present for your field's key, pass it to SetDefaultValue so the scanned value carries through onto the rendered field.
Read-only fields can be registered on the correction screens through these two events:
SFPBCGetFormElements14FDW.Codeunit.al
[IntegrationEvent(false, false)]
local procedure OnGetConsumptionCorrectionCustomFields(ItemLedgerEntry: Record "Item Ledger Entry"; var CustomFields: Record TempCustomFormElements14FDW temporary)
begin
end;
[IntegrationEvent(false, false)]
local procedure OnGetOutputCorrectionCustomFields(ItemLedgerEntry: Record "Item Ledger Entry"; ProductionOrderLine: Record "Prod. Order Line"; var CustomFields: Record TempCustomFormElements14FDW temporary)
begin
end;
TempCustomFormElements record
All of the events above pass a TempCustomFormElements14FDW temporary record. This table stores the definition of every custom field and is parsed and returned to the React frontend so it can render the fields on screen.
Helper functions
| Function | Description |
|---|---|
AddNewCustomFormElement(FieldKey, FieldName, ElementType, RequiredValue, VisibleValue, IsInvalidValue) | Initializes a new custom form element. FieldKey is a unique camelCase identifier; FieldName is the label shown in the app. |
SetDefaultValue | Sets the default value for the current custom form element (text or date). |
SetNumberMinValue / SetNumberMaxValue | Sets the minimum/maximum acceptable value for a numeric field. |
SetNumberMaxDecimalPlaces | Sets the maximum number of decimal places accepted for a numeric field. |
SetTextAreaRowsAndMaxLength | Sets the number of rows and maximum length for a text area field. |
AddSelectOption | Adds a selectable option to a select field. |
AddCustomFormElementErrorValidation | Adds a custom validation error message for the field. |
ElementType (FormElementTypes14FDW)
FormElementTypes14FDW defines the supported field types:
- Text — text field input
- Number — text field input formatted to only accept numbers
- Date — date picker input
- Select — select box with multiple options
- TextArea — large text field input
- ReadOnly — read-only field that cannot be modified
- Boolean — Yes/No field
Example: defining custom form elements
The following example defines a required text field and a select field on the Register Output screen:
[EventSubscriber(ObjectType::Codeunit, Codeunit::SFPBCGetFormElements14FDW, OnAddCustomFormElementsForOutput, '', false, false)]
local procedure AddCustomFormElementsForOutput(var TempCustomFormElements: Record TempCustomFormElements14FDW temporary)
begin
TempCustomFormElements.AddNewCustomFormElement('caseNo', 'Case No.', FormElementTypes14FDW::Text, true, true, false);
TempCustomFormElements.AddNewCustomFormElement('shift', 'Shift', FormElementTypes14FDW::Select, false, true, false);
TempCustomFormElements.AddSelectOption('1', 'First shift');
TempCustomFormElements.AddSelectOption('2', 'Second shift');
end;
Custom field validation
Custom fields can be validated using one of the two events defined in SFPBCProductionReg14FDW.Codeunit.al:
OnValidateCustomFormElementsForConsumptionOnValidateCustomFormElementsForOutput
Both events pass the TempCustomFormElements record. Use the FieldKey you defined earlier to find the correct record; the Field Value field holds the user-entered value.
These events run after the frontend's own checks — a field registered with RequiredValue = true on AddNewCustomFormElement already blocks submission of a blank value, so validation here doesn't need to re-check for a value being present. Use it for rules the frontend can't enforce on its own, such as format or business-rule checks.
[EventSubscriber(ObjectType::Codeunit, Codeunit::SFPBCProductionReg14FDW, OnValidateCustomFormElementsForOutput, '', false, false)]
local procedure ValidateCustomFormElementsForOutput(var TempCustomFormElements: Record TempCustomFormElements14FDW)
var
CaseNoLengthErr: Label 'Case No. must be exactly 6 characters.';
begin
if not TempCustomFormElements.Get('caseNo') then
exit;
if StrLen(TempCustomFormElements."Field Value") <> 6 then
TempCustomFormElements.AddCustomFormElementErrorValidation('caseNo', CaseNoLengthErr);
end;
Custom field logic
Finally, define what to do with the values entered into custom fields through the OnAfterPostProductionRegistration event. This event passes the TempCustomFormElements table along with the Item Ledger Entry number generated by the registration posting.
ItemLedgerEntryNois0in two situations, and the event is raised in both. Always test for0before using the value, as in the example below.- The registration failed. The event is raised whether or not the posting succeeded, and
0is how a failed posting is reported. Do not treat the event as confirmation that anything was posted.- No Item Ledger Entry was created. Output registered on a routing operation that is not the last operation of the routing produces only a Capacity Ledger Entry, so there is no Item Ledger Entry to write to. If you need to store a custom value against intermediate operations, write it to your own table keyed on the production order, line, and operation instead of to the Item Ledger Entry.
[EventSubscriber(ObjectType::Codeunit, Codeunit::SFPBCProductionReg14FDW, OnAfterPostProductionRegistration, '', false, false)]
local procedure OnAfterPostProductionRegistration(var TempCustomFormElements: Record TempCustomFormElements14FDW; ItemLedgerEntryNo: Integer)
var
ItemLedgerEntry: Record "Item Ledger Entry";
begin
if ItemLedgerEntryNo = 0 then
exit;
ItemLedgerEntry.Get(ItemLedgerEntryNo);
if TempCustomFormElements.Get('caseNo') then begin
// "Case No." is assumed here to be a custom field added to Item Ledger Entry via a table extension
ItemLedgerEntry."Case No." := TempCustomFormElements."Field Value";
ItemLedgerEntry.Modify();
end;
end;