Release Notes 2026-08-16
- 1 Version Information
- 2 Retirement of OpenEdge 11.7
- 3 OpenEdge 13.0 support!
- 4 OpenEdge 12.8 support!
- 5 OpenEdge 12.2 support!
- 6 PASOE / OEMANAGER VS Code Extension
- 7 Introducing the SmartComponent Library developer forum
- 8 Release Notes
- 9 New Versions of Proparse .NET Assemblies (30 December 2018)
- 10 New Version of the Consultingwerk.SmartComponents Assemly (09 January 2019)
- 11 Hybrid Realm now using IAuthenticationService
- 12 Web Handler overview
- 13 UTF-8 based deployments available
- 14 New Feature Documentation
- 15 Download Link
- 16 Overview of included tickets
- 16.1 RESTful services error handling improved: typical runtime errors now return HTTP 400 responses
- 16.2 New Security Item Authorization Report shows the authorization result of a security item for all users
- 16.3 Object Master Designer generates Logic Object and server-side event handler classes and correctly typed handler methods
- 16.4 SmartBusinessEntityAdapter can now submit a single modified record while the user interface transaction stays open
- 16.5 New developer menu entry added for maintaining the security settings of a repository based form
- 16.6 New MCP tools create menu functions that launch a Desktop or Web form
- 16.7 New Build.One SWAT connector, built on the new Data Service Interface foundation
- 16.8 Support Snapshot: session diagnostics and error stack traces can be exported in a single action
- 16.9 Ten new targeted MCP tools allow safe partial repository object updates without destroying omitted object graph parts
- 16.10 ToonConverter now supports JSON arrays, and the array returning MCP tools use TOON
- 16.11 Configuration property values can now be stored encrypted
- 16.12 Business Entity regeneration now detects stale generated files and offers them for deletion
- 16.13 Object Master Maintenance: object instances can now be filtered by their parent instance
- 16.14 Renaming or moving temp-tables and the ProDataset in the Business Entity Designer now updates all references
- 16.15 Unbound viewer field values can now be read and written from server-side event handlers
- 16.16 Pure ABL Proparse engine now resolves record and field references inside arbitrary statements
- 16.17 Business Entity Designer now suggests data-relations when a database table is added to a design
- 16.18 Business Entity Designer: temp-tables can now be deleted from the design canvas context menu
- 16.19 New MCP server tool returns the SmartDB configuration value and the current back end Git branch
- 16.20 Fixed: The encryption password file is now stored in the user's home directory instead of a folder named "~"
- 16.21 Fixed: A missing or invalid MCP auth key now returns a meaningful error instead of a raw ABL Base64 error
- 16.22 MCP auth key generator now runs in character (TTY) sessions
- 16.23 Business Entity generation via MCP can now target multiple projects through an optional projectName parameter
- 16.24 Fixed: BusinessEntityDesignerSettings is free of .NET dependencies - saving moved to BusinessEntityDesignerSettingsWriter
- 16.25 PASOE-capable code now uses Consultingwerk.Environment:NewLine instead of the .NET System.Environment:NewLine
- 16.26 Business Entity Designer non-GUI code now uses a .NET-free ABL enum for message images
- 16.27 Fixed: Call sites of FileHelper:EnsureFileNameCase are now guarded by DotNetAccessible preprocessor blocks
- 16.28 Fixed: System.Threading.Thread:Sleep in BusinessEntityGenerator:OpenFilesInIDE is now guarded for non-.NET sessions
- 16.29 Fixed: Business Entity Generator and plugins locate template files with forward slashes, so templates resolve on Linux
- 16.30 Fixed: Business Entity Generator, BatchGenerator and plugins no longer build file paths with backslash separators
- 16.31 Business Entity Designer server path migrated from proparse .NET to IProparseApi
- 16.32 Fixed: ui-catch.i no longer swallows errors in server-side flows
- 16.33 ServiceLoader now supports service definitions in JSON files in addition to XML
- 16.34 RESTful Business Entity services support ETag and If-Match for optimistic concurrency control
- 16.35 Fixed: Business Entity Designer MCP writes the .bedgm file below the configured SourceCodeBaseFolder
- 16.36 Server-side Business Entity Designer plugins can now modify the design before generation
- 16.37 Fixed: The pure ABL Proparse engine now supports the full set of documented &IF preprocessor functions
- 16.38 ProparseAbl now reports file name, line and column on every parse failure via the new ParsingException class
- 16.39 Fixed: Mermaid diagrams now render the INTEGER data type as "integer" instead of "int"
- 16.40 ADM2 Frame Migration parser classes migrated to IProparseApi so the MCP services load on Linux
- 16.41 ADM2 Frame Migration trigger and lookup processors migrated to IProparseApi
- 16.42 Business Entity Designer code generation performance: redundant Proparse parses and AST overhead reduced
- 16.43 Stray trailing blanks removed from event name CASE labels in SmartViewerLogic
- 16.44 Object Master Designer generates server-side event handler methods for toolbar button ToolClick actions
- 16.45 Fixed: Excel export into a template with pre-formatted rows no longer crashes or corrupts the workbook
- 16.46 Fixed: Initializing event handler methods of a viewer logic class are now invoked
- 16.47 Fixed: ExcelUtility:GetHeaderRow now returns the row at the requested index
- 16.48 Fixed: Regenerating an unchanged Business Entity no longer dirties the Data Access class
- 16.49 Business Entity Designer profiling harness fixture moved into the UnitTests project
- 16.50 JSON Schema for the .applicationsettings and .restapplicationsettings configuration files
- 16.51 Copy Menu Path button added to the Menu ribbon tab of the Menu Maintenance
- 16.52 New Toolbar Button Definition Editor maintains the Desktop and Web toolbar / ribbon button JSON files at design time
Version Information
OpenEdge Release | Version Information |
|---|---|
11.7.21, 12.2.19, 12.8.9, 12.8.11, 13.0.0 | Release 11.7 #89469@2026-08-16 |
Retirement of OpenEdge 11.7
OpenEdge 11.7 has been retired by Progress Software on April 1st 2025. In consequence we urge all customers still using OpenEdge 11 to upgrade to OpenEdge 12.2 or 12.8 as soon as possible.
We are intending to drop the official support of the SmartComponent Library for OpenEdge 11.7 during the second half of the year 2025. We will then no longer ensure that new features of the framework will be usable with OpenEdge 11.7
Customers that are concerned by this intention, should reach out to us as soon as possible, so that we can discuss about a solution.
OpenEdge 13.0 support!
We are pleased to announce that we are not aware of any issues using the SmartComponent Library on OpenEdge 13.0. We hereby declare OpenEdge 13.0 fully supported for the SmartComponent Library as of the release of February 15th 2026.
OpenEdge 12.8 support!
We are pleased to announce that we are not aware of any issues using the SmartComponent Library on OpenEdge 12.8. We hereby declare OpenEdge 12.8 fully supported for the SmartComponent Library as of the release of January 15th 2024.
OpenEdge 12.2 support!
We are pleased to announce that we are not aware of any issues using the SmartComponent Library on OpenEdge 12.2. We hereby declare OpenEdge 12.2 fully supported for the SmartComponent Library as of the release of April 09th 2020.
PASOE / OEMANAGER VS Code Extension
Our first VS Code extension is available in the VS Code marketplace: https://marketplace.visualstudio.com/items?itemName=ConsultingwerkApplicationModernizationSolutionsLtd.oemanager
The extension supports developers with PASOE related routine tasks during development:
checking agent or session status
trimming the PASOE agents
restarting a new agent vai the ping request
Introducing the SmartComponent Library developer forum
Got questions about using the SmartComponent Library? Wish to discuss feature requests with other users of the SmartComponent Library? To facilitate networking in the growing number of SmartComponent Library users and developers, we've added a developer forum on our new home-page. The developer forum can be found at: https://www.consultingwerk.com/support/forum
A login is required to participate in the forum. Don't be shy - create your account today!
Release Notes
Following the release of the OpenEdge 11.7 Release we have completed our testing of the SmartComponent Library on this release of Progress and are proud to announce formal support for the SmartComponent Library on OpenEdge 11.7 with the 2017-04-24 release of the SmartComponent Library. Generally we recommend customers on OpenEdge 11 to upgrade to OpenEdge 11.7 quickly. Starting the 2017-07-14 release we are supporting OpenEdge 11.7 Service Pack 1 (11.7.1).
Starting the 2015-10-09 release the Business Entity Designer will feature a start page with links to most recent release note articles on this site.
With SCL-751 we have adopted native Enums for OpenEdge 11.6. To activate this feature customers must add a definition to products.i. SCL-751 provides details for this.
With SCL-1113 we have modified the templates for SmartWindowForms and SmartTabFolderPages generated from the Business Entity Designer. We have removed the initialization of the viewer controls SmartTableIOState property (value was set to "NoDataSource" within the InitializeComponent method). Customers using their own set of templates should adopt the same fix to their templates.
New Versions of Proparse .NET Assemblies (30 December 2018)
We have updated proparse with further enhancements in this release (see https://github.com/consultingwerk/proparse/releases/tag/4.0.1.1177). Please download the .NET Assemblies ZIP file from our ESD and replace the proparse.net.dll in your environment with the latest one. The new assembly reference is:
|
As proparse is used by our Business Entity Generator, this new version of the proparse.net Assembly is also relevant for customers not doing GUI for .NET development and require support for parsing new ABL language constructs.
This version of proparse provides additional debug information when errors are thrown from proparse.net.dll.
New Version of the Consultingwerk.SmartComponents Assemly (09 January 2019)
To support the new SmartFilterControl and the "Filter" link between the SmartFilterControl/SmartDynamciFilterControl and a SmartBusinessEntityAdapter or SmartDatasetChildAdapter we have updated the Consultingwerk.SmartComponents.dll. The new Assembly reference is:
<assembly name="Consultingwerk.SmartComponents, Version=2.2.0.49856, Culture=neutral, PublicKeyToken=10603cb31df311b3"/>Customers need to update this Assembly in order to compile the latest releases of the SmartComponent Library.
Hybrid Realm now using IAuthenticationService
We've updated the SmartHybridRealm class used for http authentication to leverage the IAuthenticationService. It is therefor required that for AppServers requiring http Authentication (e.g. REST or Web) the correct IAuthenticationService implementation is registered (using service.xml files).
Customers relying on the previous behavior should ensure that the Consultingwerk.SmartFramework.Authentication.AuthenticationService is regsitered as the IAuthenticationService. This implementation will auhtenticate users against the password stored in the SmartUser database table.
When users with no Login Company set should be able to login using the SmartHybridRealm, it's required to set the Login Company Tennant Domain (similar to the field in the Login Company Maintenance) in the .restapplicationsettings / .applicationsettings, e.g.:
"DefaultAuthenticationDomain": "consultingwerk" |
This is required as the Hydrid Realm Interface from Progress does not provide the domain name to the authentication method.
Web Handler overview
This is an overview of the web handlers supported by this release
defaultHandler=OpenEdge.Web.CompatibilityHandler
handler1=Consultingwerk.OERA.JsdoGenericService.WebHandler.CatalogWebHandler: /Catalog/{EntityName}
handler2=Consultingwerk.OERA.JsdoGenericService.WebHandler.CatalogsWebHandler: /Catalogs/{PackageName}
handler3=Consultingwerk.OERA.JsdoGenericService.WebHandler.CountWebHandler: /Resource/{EntityName}/count
handler4=Consultingwerk.OERA.JsdoGenericService.WebHandler.ResourceSubmitWebHandler: /Resource/{EntityName}/SubmitData
handler5=Consultingwerk.OERA.JsdoGenericService.WebHandler.InvokeMethodWebHandler: /Resource/{EntityName}/{MethodName}
handler6=Consultingwerk.OERA.JsdoGenericService.WebHandler.ResourceWebHandler: /Resource/{EntityName}
handler7=Consultingwerk.OERA.JsdoGenericService.WebHandler.BusinessServicesWebHandler: /BusinessServices/{OutputFormat}/{PackageName}
handler8=Consultingwerk.Web2.WebHandler.SmartMenuWebHandler: /SmartMenu/{MenuStructureId}
handler9=Consultingwerk.Web2.WebHandler.SmartMenuStructureWebHandler: /SmartMenuStructure
handler10=Consultingwerk.Web2.WebHandler.SmartRoutesWebHandler: /SmartRoutes
handler11=Consultingwerk.Web2.Services.SmartViewsHandler.SmartGridWebHandler: /SmartViews/Grid/{EntityName}/{ViewName}/{DetailTemplate}
handler12=Consultingwerk.Web2.Services.SmartViewsHandler.SmartGridWebHandler: /SmartViews/Grid/{EntityName}/{ViewName}
handler13=Consultingwerk.Web2.Services.SmartViewsHandler.SmartGridWebHandler: /SmartViews/Grid/{CustomViewName}
handler14=Consultingwerk.Web2.Services.SmartViewsHandler.SmartViewerWebHandler: /SmartViewer/Viewer/{EntityName}/{ViewName}
handler15=Consultingwerk.Web2.Services.SmartViewsHandler.SmartViewerWebHandler: /SmartViewer/Viewer/{ObjectName}
handler16=Consultingwerk.Web2.Services.SmartViewsHandler.SmartFormWebHandler: /SmartForm/{FormTemplate}/{EntityName}/{ViewName}
handler17=Consultingwerk.Web2.Services.SmartViewsHandler.SmartFormWebHandler: /SmartForm/{FormTemplate}/{ObjectName}
handler18=Consultingwerk.Web2.WebHandler.SmartMessageWebHandler: /SmartMessage/{MessageGroup}/{MessageNumber}
handler19=Consultingwerk.Web2.WebHandler.GetImageWebHandler: /Image/{FileName}
handler20=Consultingwerk.Web2.WebHandler.SmartValueListWebHandler: /ValueList/{ValueList}
handler21=Consultingwerk.Web2.WebHandler.SmartAttachmentsWebHandler: /Attachments/{Table}/{KeyValues}
handler22=Consultingwerk.Web2.WebHandler.SmartAttachmentWebHandler: /Attachment/{Guid}
handler23=Consultingwerk.Web2.WebHandler.SessionContextWebHandler: /SessionContext
handler24=Consultingwerk.Web2.WebHandler.ContextPropertiesWebHandler: /ContextProperties/{PropertyName}
handler25=Consultingwerk.Web2.WebHandler.ContextPropertiesWebHandler: /ContextProperties
handler26=Consultingwerk.Web2.WebHandler.SessionInfoWebHandler: /SessionInfo
handler27=Consultingwerk.Web2.WebHandler.FileSearchWebHandler: /FileSearch/{FileName}
handler28=Consultingwerk.Web2.WebHandler.ExecuteAblWebHandler: /ExecuteAbl
handler29=Consultingwerk.Web2.WebHandler.SmartLanguagesWebHandler: /Languages
handler30=Consultingwerk.OERA.RestResource.RestEntitiesWebHandler: /Entities
handler31=Consultingwerk.OERA.Swagger.SwaggerWebHandler: /Swagger/{EntityName}
handler32=Consultingwerk.OERA.Swagger.SwaggerRestEntitiesWebHandler: /SwaggerEntities/{OutputType}
handler33=Consultingwerk.Web2.WebHandler.SmartTokenSecurityCheckWebHandler: /TokenSecurityCheck/{ObjectName}
handler34=Consultingwerk.Web2.WebHandler.SmartTokenSecurityCheckWebHandler: /TokenSecurityCheck
handler35=Consultingwerk.Web2.WebHandler.SmartEntityTableMappingHandler: /EntityTableMapping/{EntityName}/{TableName}/{UiTypeCodes}
handler36=Consultingwerk.Web2.WebHandler.SmartTreeRootNodeWebHandler: /SmartViews/TreeRootNode/{rootnodeid}
handler37=Consultingwerk.Web2.WebHandler.SmartTreeChildNodesWebHandler: /SmartViews/TreeChildNodes/{parentnodeid}
handler38=Consultingwerk.Web2.WebHandler.SmartSecurityCheckWebHandler: /IsRestricted/{SecurityRealmCode}/{SecurityItemGuid}
handler39=Consultingwerk.Web2.WebHandler.SmartFieldSecurityCheckWebHandler: /RestrictedFields/{TableName}
handler40=Consultingwerk.Web2.WebHandler.GetImageNamesHandler: /ImageNames
handler41=Consultingwerk.Web2.WebHandler.ExecuteQueryWebHandler: /ExecuteQuery
handler42=Consultingwerk.Web2.WebHandler.TranslationProviderWebHandler: /GetTranslation |
UTF-8 based deployments available
Starting August 14th 2016 we ship our source code also as UTF-8 encoded. The ZIP files ending with _utf8.zip contain the source code encoded with that code page. The source code in the remaining archives is still encoded in ISO8859-1. Also on Github there is a new branch containing the OpenEdge 11 based source code in UTF-8.
Related article (Progress K-Base): How to get Progress Developer Studio to save in UTF-8 encoding
New Feature Documentation
Download Link
http://esd.consultingwerkcloud.com/
https://github.com/consultingwerk/SmartComponentLibrary
Overview of included tickets
RESTful services error handling improved: typical runtime errors now return HTTP 400 responses
The error handling of the RESTful services layer has been improved so that typical runtime errors — such as input data type conversion failures, missing mandatory fields, and unique key conflicts — now resolve to HTTP 400 (bad request) status codes instead of HTTP 500. Error responses can optionally be returned in an RFC 7807 compatible format, and field or table names from validation messages are exposed in their own JSON attributes. This behavior is configurable to preserve compatibility with the existing error reporting. No changes are required in customer code unless the new response format is explicitly enabled.
New Security Item Authorization Report shows the authorization result of a security item for all users
A new desktop screen, Consultingwerk.Windows.Framework.Authorization.SmartAuthorizationAssignmentReportForm, reports for a selected security realm and security item whether every user of the current login company (plus users without a login company) is restricted, and where that assignment was defined - directly on the user, through a group, or by the default. The data is provided by the new Business Task Consultingwerk.SmartFramework.Authorization.SecurityItemAuthorizationReportBusinessTask, which is consumed like a Business Entity and passes the realm, item and login company GUIDs in the query string of the FetchDataRequest. The task is written in pure ABL without .NET references, so it also runs on PASOE/Linux and can back the Angular web frontend. Results are shown in a SmartDataBrowser with Export To Excel and Export To PDF available from the ribbon.
Object Master Designer generates Logic Object and server-side event handler classes and correctly typed handler methods
The Generate Code ribbon group of the Object Master Designer now offers four actions for MetaViewer, MetaGrid, MetaForm and MetaControlGroup and their object instances: generate the Logic Object class, create a client-side event handler method, generate the server-side event handler Business Task including its GetDataset method, and create a server-side event handler method. Developers no longer have to look up handler signatures by hand; the signatures come from the new IRepositoryEventCatalogService backed by Consultingwerk/SmartFramework/Repository/repository-events.json, which projects can substitute with their own implementation. The generated method name is written back into the selected event attribute, and the Open in PDSOE / VS Code / Windsurf buttons now open the server-side event handler class as well.
SmartBusinessEntityAdapter can now submit a single modified record while the user interface transaction stays open
The SmartBusinessEntityAdapter received a new SubmitRecord method that sends the changes of a single record to the backend while AutoCommit is disabled and the surrounding user interface transaction remains active, for example smartBusinessEntityAdapter1:SubmitRecord (buffer eOrderLine:handle). The record may belong to the adapter's own EntityTable or to any ViewTable managed by a linked SmartDatasetChildAdapter. A changes dataset containing only that row is built and submitted, error handling including questions and input prompts is performed as usual, and the response is merged back into the front end. After the submit the adapter re-evaluates whether any changes remain and resets the transaction state when the dataset is clean again.
New developer menu entry added for maintaining the security settings of a repository based form
The runtime repository developer menu now offers a Security Settings dialog for the current repository based form, available to security administrators. The new form Consultingwerk.Windows.Framework.Authorization.FormAuthorizationMaintenanceForm provides five tabs covering launch permission (SOM), tab folder security tokens (SST), toolbar items (STI), field enabled and visible settings (SFS/SFV) and field group settings (SFGS/SFGH), each maintained per selected user, group or Everybody using the tri-state Restricted / Unrestricted / Default editing known from the menu security maintenance. The structural information and the authorization data are provided by the new Business Task Consultingwerk.SmartFramework.Authorization.FormAuthorizationBusinessTask, whose methods are annotated with @RestMethod and parameter schemas so they can also be consumed from the Angular web application. Missing security items and tokens can be created directly from the dialog, and the security assignment verification can be opened pre-filled for the selected realm and item.
New MCP tools create menu functions that launch a Desktop or Web form
Two new MCP tools, create_desktop_menuitem and create_web_menuitem, create a SmartFunction menu function that launches either a GUI for .NET form or an Angular/Web view, and optionally the matching SmartMenu menu item below a given menu structure node. The shared logic lives in the new Consultingwerk.SmartFramework.Menu.MenuItemDesignBusinessTask, which returns the GUIDs of the created records. The menu structure is addressed either by its GUID or by a backslash separated menu structure path, and both records are created in a single transaction.
New Build.One SWAT connector, built on the new Data Service Interface foundation
A new Web Handler in the package Consultingwerk.Integrations.BuildOne.Swat connects the Build.One SWAT framework to SmartComponent Library Business Entities. It translates the Build.One JSON fetch and update requests into SmartComponent Library standards such as QueryExpression and ListQueryExpression and delegates to the OERA Service Interface, through the endpoints /service/swat/api/openedge/business-entity/fetch and /service/swat/api/openedge/business-entity/update. The connector is the first implementation of the new abstract package Consultingwerk.OERA.DataServiceInterface, which is intended to replace the JsdoGenericService Service and ResourceWebHandler classes in the long term; the conversion of a JSON payload into a ProDataset with before-image representation is exposed as a public method of the abstract service class so other integrations can reuse it. Build.One supports single-table Business Entities only, and the access token it sends is exposed as a property of the service class for the duration of the request. Technical documentation including the request semantics and the operator reference is available on Confluence under OERA / Integrations.
Support Snapshot: session diagnostics and error stack traces can be exported in a single action
The SessionInfoForm has a new Support Snapshot action that copies all collected sections to the clipboard or saves them to a file in one pass, instead of copying single values as before: AVM information, database connections, startup parameters, services and session properties. The error dialog offers the same snapshot through the existing ErrorMessageFormButtonProvider, extended by the error message text, the error object and the stack trace. The snapshot is produced both as a readable text block and as JSON, built on the existing JsonSerializable support of Consultingwerk.SessionInfo, carries a timestamp and masks sensitive values such as -sectok and -P using the masking already applied by GetStartupParameters. The existing single value copy and display behaviour is unchanged. Customers can therefore provide support with the complete context on first contact.
Ten new targeted MCP tools allow safe partial repository object updates without destroying omitted object graph parts
Ten new MCP procedures were added to Consultingwerk/SmartFrameworkMcpServer, each performing one narrowly scoped repository change: update_repository_attributes, create_object_master, create_object_instance, move_object_instance, delete_object_instance, create_link, delete_link, create_page, update_page and delete_page. Because every tool only touches the part of the object graph it addresses, agents and API consumers can no longer accidentally remove instances, links, pages or attribute values by sending a partial payload. All Object Master and Object Instance parameters accept either a name or a GUID, attribute values are transported as JSON or TOON, and a JSON null resets an attribute to its inherited value. Supporting APIs were added to Consultingwerk.SmartFramework.Repository.ISmartRepositoryService (attribute reset, instance move and delete with cascade, container endpoint links, link delete, and the full page create/update/delete set); the existing update_repository_object tool keeps its full payload replace behaviour, so no changes are required in customer code.
ToonConverter now supports JSON arrays, and the array returning MCP tools use TOON
Consultingwerk.Framework.Toon.ToonConverter now accepts a JsonArray in Convert, including the tabular encoding of uniform object arrays defined by TOON v3.0, which is exactly the shape that saves the most tokens. In the parsing direction ToJson now returns a JsonConstruct, and the two new typed wrappers ToJsonObject and ToJsonArray return the concrete construct and raise a meaningful error when the document root is of the other type. Nine array returning MCP procedures, among them get-database-tables, get-repository-objects and search-database-tables, now emit TOON, so the whole tool surface is consistent. Two conformance fixes change the TOON text produced for existing payloads: empty arrays are now encoded as key: [] in field position and [] at root (the decoder still accepts the legacy forms), and arrays nested inside list items round-trip correctly. Existing code assigning the result of ToJson to a JsonObject must be changed to ToJsonObject.
Configuration property values can now be stored encrypted
Selected property values in the JSON configuration files, such as passwords, API keys and the clientPrincipalSeal value, can now be stored encrypted on disk instead of in plain text. Encrypted values are recognized and decrypted by the configuration service, which is the only place holding the clear-text value, so implementations of IJsonConfiguration - for example the one printing the configuration into the PASOE log at session startup - cannot expose them accidentally. The key and the algorithm are accessed through a service, so the storage mechanism for the key can be exchanged, for instance a SmartDB configuration value or the database GUID.
Business Entity regeneration now detects stale generated files and offers them for deletion
All ABL class and include files produced by the Business Entity Generator and its plugins now carry the @BusinessEntityGenerator file annotation with the entity name and the file type. The new public method BusinessEntityGenerator:ScanForStaleFiles uses a fast RegEx based scan of the source tree to find annotated files of the entity and marks those that are no longer part of the expected output; a new static CheckStaleFiles event lets each plugin flag the file types it owns, so obsolete predicate, request, model and REST classes are recognized as well as renamed include files and complete file sets left behind in a previous folder. In the GUI Business Entity Designer the candidates are presented after a successful regeneration in a dialog listing file name, file type and last modified timestamp, with multi-select and Select All / Select None. No file is deleted without explicit user confirmation, Cancel deletes nothing, and confirmed deletions also remove the sibling .r and .annotations files. Version 1 runs in the GUI client only, the scan is not executed on the AppServer.
Object Master Maintenance: object instances can now be filtered by their parent instance
The Instances tab of the Object Master Maintenance form has a new Filter by Parent control next to the existing Filter by Page combo, which makes the nesting of object instances much easier to follow. The read-only editor opens a drop-down hosting a tree of all instances of the Object Master, built by the new UserControl Consultingwerk.Windows.Framework.Repository.Object.ParentInstanceFilterTreeControl. Selecting the root filters for instances placed directly in the container, selecting a container-type instance filters for its children, and SplitContainer instances offer additional Left and Right nodes filtering by the layout position. A second editor button resets the filter, the tree is rebuilt every time the drop-down opens, the parent filter combines with the page filter and is reset when a different Object Master record is selected.
Renaming or moving temp-tables and the ProDataset in the Business Entity Designer now updates all references
The Business Entity Generator now detects renamed or moved temp-table and ProDataset include files at the very beginning of a regeneration, before any file is written and while the old include files are still parseable. References to a renamed temp-table are updated through the IProparseApi service abstraction and targeted literal replacement, covering the DataAccess class (AttachDataSource, DetachDataSource, SourceColumn), the static query include file, dataset callback registrations and custom code. A renamed or moved dataset include is handled by a text based rewrite of the include references in all files belonging to the Business Entity. Every rewritten file is announced through the existing WriteFile events, and the obsolete include files surface in the stale file dialog. The implementation is pure ABL, so the scenario works both in the GUI Designer and in the MCP server through regenerate_business_entity. No changes required in customer code.
Unbound viewer field values can now be read and written from server-side event handlers
Values of unbound viewer fields are now transported to the server-side event handler in a new UnboundValues property of EventHandlerParameter, a DictionaryPrimitiveHolder with a character key in the form <viewername>.<fieldname> and an IPrimitiveHolder value. Updated values are returned to the client through the matching UnboundValues property of UiControl. In the GUI for .NET the values are gathered by the EventHandlerParameterFactory and applied back by SmartViewerUiControlHandler, and FieldControl updates such as enabled, disabled, style, label or visible now also reach unbound components. For the Angular front end the EventDispatcherService deserializes and serializes the dictionary. Server-side event handlers can therefore access unbound field values directly, without a logic class or an additional calculated field.
Pure ABL Proparse engine now resolves record and field references inside arbitrary statements
The pure ABL Proparse engine (ProparseAblApi) previously materialized record and field references only for the statement heads structured by the statement framer, so consumers navigating below the statement head had to use the .NET/IKVM engine. The general statement grammar was implemented in Parser/AblParser and the tree parser was extended to resolve the additional references against the schema, bringing the ABL engine close to feature parity with ProparseDotNetApi. The parity harness now compares whole subtrees and the resolved RECORD_NAME / Field_ref references rather than only the top level statement head sequence. As a result, syntax tree based refactorings work on PASOE/Linux and in the MCP server as well; developers can select the ABL engine on those platforms without an external ProparseWeb service. Note that temp-table names carried in string literals remain invisible to any syntax tree and still require a text based pass.
Business Entity Designer now suggests data-relations when a database table is added to a design
A new Business Entity Designer plugin, Consultingwerk.BusinessEntityDesigner.Plugins.DataRelationAdvisorPlugin, detects possible data-relations whenever a database table is dropped onto a design that already contains temp-tables. Matching is performed on the temp-table index definitions of the design, and the developer is either asked a simple Yes/No question for a single candidate or presented with the DataRelationAdvisorForm dialog to pick the source temp-table when several candidates exist. The relation itself is created through BusinessEntityDatasetController:CreateDataRelation, so relation naming, the IDataRelationCustomizer hook and the diagram connector all behave as usual. The GUI free detection logic lives in DataRelationCandidateService so it can be reused later by MCP clients; the plugin can be switched off by removing its entry from the BusinessEntityDesignerPlugins setting.
Business Entity Designer: temp-tables can now be deleted from the design canvas context menu
The context menu of the Business Entity Designer design canvas now offers a Delete Temp-Table entry; previously a temp-table could only be removed by selecting its shape and pressing the Del key. The entry is enabled only when exactly one temp-table is selected and shows the name of the temp-table before the deletion is carried out. Deletion is routed through BusinessEntityDatasetController:RemoveTable, so the temp-table and all data-relations connected to it are removed exactly as before. No changes required in customer code.
New MCP server tool returns the SmartDB configuration value and the current back end Git branch
A new MCP server tool has been added that returns environment information about the back end the MCP server is connected to: the SmartDB configuration value identifying the database configuration the back end runs against, and the Git branch name of the back end code base. MCP clients can call this tool to confirm which branch they are working in and which branch the SmartDB belongs to, and can warn the user when the two do not match. No changes required in customer code; the tool is available through the standard MCP tool list.
Fixed: The encryption password file is now stored in the user's home directory instead of a folder named "~"
EncryptionPasswordProvider no longer relies on the shell convention that a leading tilde is expanded to the user's home directory, which caused a directory literally named "~" to be created below the current working directory on Linux and in Windows sessions without .NET. The home directory is now resolved explicitly, so the .smartframeworkmcpserver-encryptionpassword file is written to a stable per-user location regardless of the working directory the session was started from, and previously generated MCP auth keys remain decryptable. Existing stray "~" directories can be removed manually; the auth key needs to be regenerated once after the update. No changes required in customer code.
Fixed: A missing or invalid MCP auth key now returns a meaningful error instead of a raw ABL Base64 error
The MCP server credential service now validates the supplied auth key before using it, so a missing SMARTFRAMEWORKMCP_AUTH_KEY and a syntactically invalid or non-decryptable key are reported as distinct, actionable authentication errors instead of a low-level Base64, decryption or JSON parse error. The supplied key value is never echoed back in the error message. As the validation happens centrally in the credential service, all MCP tools benefit from it. No changes required in customer code.
MCP auth key generator now runs in character (TTY) sessions
gen-smartframeworkmcp-auth-key.p now adapts its user interface to the session type instead of assuming a GUI session. In a character (TTY) session the generated auth key is presented using a MESSAGE ... VIEW-AS ALERT-BOX so that it fits into a standard 80 column terminal, while Windows GUI sessions keep the editor widget that allows the key to be selected and copied with the mouse. The fixed default window width was removed and the duplicated colons in the side labels of the input frame were corrected. Developers can now generate an MCP auth key directly on Linux without a graphical session; no changes are required in customer code.
Business Entity generation via MCP can now target multiple projects through an optional projectName parameter
The MCP server tools that generate or modify Business Entities (create_businessentity, regenerate_business_entity, add_temp_table, add_calculated_field, remove_field) accept a new optional projectName input, so generated source code no longer always lands in the single project whose .BusinessEntityDesignerSettings.xml happens to be first in the PROPATH. Project names are mapped to folders through the new McpServerProjects configuration property in .applicationsettings / .restapplicationsettings (for example ABL, Tests and UnitTests), and all output folders for source code, include files and r-code are resolved from that project's settings file. When the parameter is left empty for operations on an existing Business Entity, the settings file closest to its .bedgm file is now used - same folder, then parent folders - which is enabled by the new reusable FileHelper:FindFileInFolderOrParents method; create_businessentity keeps the previous PROPATH behaviour. Unknown project names and misconfigured project folders raise dedicated exceptions with meaningful messages. No changes are required in customer code; the optional parameter is backward compatible with older MCP server versions.
Fixed: BusinessEntityDesignerSettings is free of .NET dependencies - saving moved to BusinessEntityDesignerSettingsWriter
The settings saving logic of Consultingwerk.BusinessEntityDesigner.BusinessEntityDesignerSettings, whose write-protection error handling referenced the GUI-only MessageFormImages class, was moved into a separate BusinessEntityDesignerSettingsWriter class. The settings class itself now compiles without the .NET framework, which is required because it steers the Business Entity generator, the BatchGenerator, the server plugins and the MCP Business Task on PASOE / Linux. Code that saves the Business Entity Designer settings or plugin settings should use the new BusinessEntityDesignerSettingsWriter class.
PASOE-capable code now uses Consultingwerk.Environment:NewLine instead of the .NET System.Environment:NewLine
All framework code that may run on PASOE / Linux - including the Business Entity Designer services, generator and abstract plugin base classes - now uses the platform-neutral static property Consultingwerk.Environment:NewLine instead of the .NET reference System.Environment:NewLine, which is not available without the .NET framework. GUI-only namespaces such as Consultingwerk.Windows.*, Consultingwerk.SmartComponents.*, Consultingwerk.WindowIntegrationKit.* and the demos are unaffected and keep using System.Environment:NewLine. Developers writing code intended to run server-side should use Consultingwerk.Environment:NewLine accordingly.
Business Entity Designer non-GUI code now uses a .NET-free ABL enum for message images
The message images used with the MessageFormHelper APIs are now available as a .NET-free ABL enum. All non-GUI Business Entity Designer code - including the abstract plugin base classes in Consultingwerk.BusinessEntityDesigner.Common.Plugins and the AppServer plugin variants in Consultingwerk.BusinessEntityDesigner.Server.Plugins - was converted from the GUI-only MessageFormImages constants to the new enum, so these classes compile on PASOE / Linux without the .NET framework. The image parameters of the MessageFormHelper overloads remain typed Progress.Lang.Object, so existing GUI code passing MessageFormImages constants continues to work unchanged. When writing code that may run server-side, use the new enum instead of MessageFormImages.
Fixed: Call sites of FileHelper:EnsureFileNameCase are now guarded by DotNetAccessible preprocessor blocks
The calls to FileHelper:EnsureFileNameCase in BusinessEntityGenerator are now wrapped in &if defined (DotNetAccessible) preprocessor blocks, matching the guard on the method itself, so the generator compiles in sessions without the .NET framework (PASOE / Linux). Skipping the call there is semantically correct, as the method only corrects on-disk file name casing on case-insensitive file systems. No changes required in customer code.
Fixed: System.Threading.Thread:Sleep in BusinessEntityGenerator:OpenFilesInIDE is now guarded for non-.NET sessions
The unguarded System.Threading.Thread:Sleep call in BusinessEntityGenerator:OpenFilesInIDE is now wrapped in an &if defined (DotNetAccessible) preprocessor block, so the generator compiles in sessions without the .NET framework (PASOE / Linux). The delay only exists to give the IDE file watcher time to detect newly generated files and is therefore not needed server-side. No changes required in customer code.
Fixed: Business Entity Generator and plugins locate template files with forward slashes, so templates resolve on Linux
Template file paths built from BusinessEntityDesignerSettings:TemplateFolder in the Business Entity Generator and its plugins now use forward slashes instead of backslashes, including the hard-coded Generator/Templates fallback path. Previously the backslash was treated as a literal file name character on Linux, so template resolution failed on PASOE. Forward slashes work on Windows as well, so behaviour there is unchanged. No changes required in customer code.
Fixed: Business Entity Generator, BatchGenerator and plugins no longer build file paths with backslash separators
All path building in BusinessEntityGenerator, BatchGenerator and the Business Entity Designer plugins now uses the forward slash as separator, which works on Windows and Linux alike. Previously mixed separators - for example an output file name joined with a backslash while the package part already used forward slashes - caused generated files on Linux to be written with a literal backslash in the file name instead of into the intended folder. Legitimate normalization calls that remove backslashes are unaffected. No changes required in customer code.
Business Entity Designer server path migrated from proparse .NET to IProparseApi
AbstractModelClassGeneratorPlugin, the AssignSkipDictionary parser classes and TempTableImporter used the proparse .NET bridge directly and could therefore not be compiled without .NET. They now use the IProparseApi abstraction, with ProparseAblApi registered for the MCP services on the AppServer and ProparseDotNetApi on the Desktop, so the Business Entity Designer server path runs on PASOE and Linux. Breaking change: BusinessEntityDatasetController, DatabaseSchemaDatasetController and TempTableImporter moved from the Consultingwerk.BusinessEntityDesigner.Services package to Consultingwerk.BusinessEntityDesigner.UI package. Customer code with fully qualified references, custom desktop designer plugins, and in particular any SmartBusinessEntityAdapter:DatasetControllerType property value naming these classes must be updated, because the latter is only detected at runtime.
Fixed: ui-catch.i no longer swallows errors in server-side flows
Consultingwerk/Windows/ui-catch.i now rethrows the error after logging it when SESSION:REMOTE is true, so failures in server-side flows - for example MCP-driven generation - are reported to the caller instead of appearing to have succeeded. Behaviour in desktop event handlers is unchanged. No changes required in customer code.
ServiceLoader now supports service definitions in JSON files in addition to XML
Consultingwerk.Framework.ServiceLoader can now load service definitions from JSON files in addition to the existing service.xml format. Files with the .json extension are parsed as JSON, every other extension keeps the previous XML behaviour, so existing configurations continue to work unchanged. The JSON format supports the same properties as the XML format (Order, ServiceTypeName, ServiceClassName, Disabled, RequiredDatabases, LazyLoading), where ServiceTypeName accepts either a comma-delimited string or a JSON array of interface names. JSON is supported by all file-based entry points: Load (single file and array of files), LoadFromFile, LoadFromFiles, Unload and UnloadFromFile.
RESTful Business Entity services support ETag and If-Match for optimistic concurrency control
A new option on the @RestAddress annotation enables RFC 7232 conditional requests for record type addresses; it is off by default, so existing resources keep their behaviour. A GET then returns an ETag response header, and PUT and PATCH require an If-Match header, answering with 412 Precondition Failed on a mismatch and 428 Precondition Required when the header is missing. A successful update returns the new ETag, so clients can chain updates without an intermediate GET. The ETag value is produced by an overridable GetETagValue method on the Business Entity base class, so applications can replace the default record hash with their own implementation, for example a version counter or a last changed timestamp. The generated Swagger document documents the ETag response header and the If-Match parameter. Enabling the option requires regenerating the .annotations file and deleting restresources-cache.json.
Fixed: Business Entity Designer MCP writes the .bedgm file below the configured SourceCodeBaseFolder
The create business entity MCP tool resolved the target path of the .bedgm design file relative to the location of the .BusinessEntityDesignerSettings.xml file instead of the SourceCodeBaseFolder configured in the Business Entity Designer settings, so the design file was created in the wrong folder. It is now written below the same base folder that the generated classes use. No changes required in customer code.
Server-side Business Entity Designer plugins can now modify the design before generation
Server-side Business Entity Designer plugins had no early hook to change the design before generation, so a plugin that adds framework default fields to every temp-table had to abuse the ValidateDesign event, which is published after the generator's own datatype, format and initial value checks and therefore bypasses them for the injected fields. New BeforeGenerateSource and BeforeRegenerateSource events are now raised at the start of GenerateCode and RegenerateCode, before the design validation, so an IBusinessEntityDesignerServerPlugin can modify the design there and the injected records still run through the generator's validation.
Fixed: The pure ABL Proparse engine now supports the full set of documented &IF preprocessor functions
The hand-written condition evaluator PreproExpression of the pure ABL Proparse engine only implemented a subset of the functions allowed in preprocessor expressions, so source files using SUBSTRING or any of the other missing functions inside an &IF or &ELSEIF condition could not be parsed at all. EvaluateFunction now covers the complete function list documented by OpenEdge, including abbreviations and argument defaults, matching the behaviour of the reference Proparse engine and the OpenEdge preprocessor; session-dependent functions such as ETIME, RANDOM, TIME, TODAY, OPSYS, PROVERSION, PROPATH, PROCESS-ARCHITECTURE, AUDIT-ENABLED, DBTYPE, LIBRARY and MEMBER are seeded consistently through the existing ProparseHelper:SetProparseEnvironment pattern. Genuinely unknown functions still raise a clear error, now reporting file name, line and column. SmartUnit coverage was added for each function, and the deviation note in proparse-abl-notes.md has been updated. No changes required in customer code.
ProparseAbl now reports file name, line and column on every parse failure via the new ParsingException class
The pure-ABL Proparse engine now includes the file name, line number and column of the failure position in every lex, preprocess and parse error message. A new ParsingException class, extending Consultingwerk.Exceptions.Exception, additionally exposes the failure position as the typed properties FileName, LineNumber and ColumnNumber. Tooling such as the Business Entity Designer, the MCP server and lint infrastructure can therefore consume the position programmatically instead of parsing message text. Developers catching parse failures can check for ParsingException and read these properties directly.
Fixed: Mermaid diagrams now render the INTEGER data type as "integer" instead of "int"
The MapDataType method of AbstractMermaidDiagramPlugin was the only mapping that abbreviated a data type name, rendering INTEGER fields as "int" while character, decimal, logical, datetime and int64 were spelled out in full. INTEGER is now mapped to "integer", so all data types are rendered consistently in the ER diagrams generated by the Business Entity Designer. No changes required in customer code; the generated diagram .md files pick up the new spelling the next time each Business Entity is regenerated.
ADM2 Frame Migration parser classes migrated to IProparseApi so the MCP services load on Linux
LegacyGuiParser, FrameParser and UserControlParser used the proparse .NET bridge directly. On Linux the in-session compile failed with error 20829, the load of AblFrameMigrationService threw a ServiceLoaderException and every Business Entity Designer MCP tool became unusable. The classes now use the IProparseApi abstraction, with ProparseAblApi serving the API on the AppServer and ProparseDotNetApi on the Desktop. No changes required in customer code.
ADM2 Frame Migration trigger and lookup processors migrated to IProparseApi
TriggerParser, ProcessTriggerNodeEventArgs, TriggerEventProcessor and the two lookup control pre-processors of the ADM2 Frame Migration used the proparse .NET bridge directly and therefore failed to compile on the AppServer with error 20829. They now use the IProparseApi abstraction, following the pattern of the parser classes. No changes required in customer code.
Business Entity Designer code generation performance: redundant Proparse parses and AST overhead reduced
A measured analysis of a regeneration run showed that a single-table Business Entity caused 28 full parses of only 13 distinct files, with the Data Access class alone parsed twelve times, because each of the eight sequential update passes of the regeneration locates its marker annotations by parsing the file again. This Epic collects the resulting improvements, first reducing the number of parses in the regeneration merge and then making the remaining parses cheaper in the annotation lookup and in the lexer. No changes required in customer code.
Stray trailing blanks removed from event name CASE labels in SmartViewerLogic
The CASE label for the AfterFieldsEnabled event in SmartViewerLogic:SubscribeControlEventHandler contained four trailing blanks, and the CASE labels in SmartViewerLogic and SmartDataBrowserLogic have been swept for further stray whitespace and verified against the event names emitted by the renderers. Behaviour was never affected because ABL ignores trailing blanks in CHARACTER comparisons, but the event names are now searchable as exact literals and safe to use as dictionary or JSON keys. No changes required in customer code.
Object Master Designer generates server-side event handler methods for toolbar button ToolClick actions
The Create SSEH Method ribbon button of the Object Master Designer now has a second mode for toolbar instances of a MetaForm or MetaControlGroup. It offers the buttons of the selected toolbar whose buttonAction starts with "server:", and then creates the named handler method in the named class, or opens the class at that method when it already exists. The button definition remains the source of truth and is not written back. A new IToolbarButtonDefinitionService encapsulates reading and parsing the Desktop and Web toolbar button definition files, and the ToolClick event was added to the repository event catalog so the generator knows the parameter interface of the method.
Fixed: Excel export into a template with pre-formatted rows no longer crashes or corrupts the workbook
Two defects in ExcelUtility surfaced whenever ExcelWorksheetExporter exported into an .xlsx template whose sheet already contained rows at the row indexes being written to. Updating a formatted template cell that carries a style but no value element raised runtime error 10068, and newly generated rows were always appended at the end of the sheet, which produced duplicate row indexes and broke the ascending row order that Microsoft Excel requires, so Excel prompted to repair the file. UpdateRowData now creates a CellValue when the cell has none, and GenerateRowData removes an existing row at the target index and restores the ascending row order after appending. No changes required in customer code.
Fixed: Initializing event handler methods of a viewer logic class are now invoked
GenericLoadHandler in SmartViewerLogic filtered its event subscription records on the event name "Load", while the records are always created with the name "Initializing" that the renderer passes in. The loop could therefore never match and the handler method was silently never called, without an error and without a log entry. The filter now matches "Initializing". Please note that this is a behaviour change: viewer logic Initializing handler methods that have never run so far will start running, so applications built around the previous behaviour should be verified.
Fixed: ExcelUtility:GetHeaderRow now returns the row at the requested index
GetHeaderRow returned the first Row element of the sheet regardless of the row index InsertHeaderRow was targeting, so on a template with header label rows at more than one index the wrong row was silently relabelled while the intended header row kept its placeholder text, without any error being raised. The target row index is now passed down and matched on Row:RowIndex. Please note that GetHeaderRow is protected, so subclasses that override it have to follow the changed signature.
Fixed: Regenerating an unchanged Business Entity no longer dirties the Data Access class
When refreshing an existing AssignSkipDictionary entry, the generator replaced only the token texts of the remainder of the statement, so the whitespace between the tokens survived and a growing run of trailing blanks accumulated with every regeneration. The Data Access class was therefore reported as changed by Git after every regeneration even when the design had not changed at all. The remainder of the statement is now cleared as one contiguous range, and regenerating an unchanged Business Entity produces a byte-identical file. No changes required in customer code.
Business Entity Designer profiling harness fixture moved into the UnitTests project
The fixture Business Entity that the code generation profiling harness regenerates lived in the untracked ABL/Test scratch area, where a routine git clean removed it and made the harness unusable from a fresh clone. The fixture is now version-controlled below UnitTests/Consultingwerk/BusinessEntityDesignerTests, next to the harness that uses it, so measurements are reproducible by any developer and on Jenkins. The harness continues to snapshot and restore the fixture around a run, so the working tree stays clean.
JSON Schema for the .applicationsettings and .restapplicationsettings configuration files
The new schema file Consultingwerk/Framework/applicationsettings.schema.json describes the known configuration settings, so that VS Code and Progress Developer Studio offer completion, hover documentation and validation, and a mistyped setting name is reported instead of being silently ignored at runtime. Both shipped configuration files reference the schema through the standard $schema property, which ConfigurationProvider removes during initialization so that it is invisible to the application. Applications that add their own settings create their own schema file and reference the SmartComponent Library schema from it. The obsolete rollbaseRestApiUrl setting was removed from all configuration files.
Copy Menu Path button added to the Menu ribbon tab of the Menu Maintenance
The Menu Maintenance (SmartMenuMaintenanceForm) now offers a Copy Menu Path button in the Menu ribbon tab that copies the path of the currently selected menu structure node or menu item to the clipboard. The path consists of backslash separated segments; per level the MenuStructureCode is preferred over the MenuName, and the artificial tree root is not part of the path. Any nesting depth is supported, and the copied value can be handed to the menu related MCP tools that address a menu structure by path.
New Toolbar Button Definition Editor maintains the Desktop and Web toolbar / ribbon button JSON files at design time
The toolbar and ribbon button definition files of the repository rendering, registered as the WindowsToolbarButtonDefinitions (Desktop) and Web2ToolbarButtonDefinitions (Web) sections of .applicationsettings / .restapplicationsettings, no longer have to be edited by hand in a text editor. A new GUI for .NET ribbon form lists every registered definition file in a combo box and shows the buttons of the selected file in a SmartDataBrowser, deriving the front end from the configuration entry the file is registered under. Ribbon buttons Add, Update and Remove maintain the buttons through an InputPromptHelper:ComplexInput dialog with the property set of the respective front end and write the file back preserving the JSON member order and the surrounding tabs / groups / $schema sections, so only the properties actually filled in are written and the resulting diff stays small. The RibbonGroup drop-down offers the union of the group keys of all registered Desktop definition files, matching the way the renderer resolves them, and a further ribbon button opens the selected file in the current code editor through IIdeSupportService:OpenFile (PDSOE, VS Code or Windsurf). IToolbarButtonDefinitionService introduced in SCL-5669 was extended with per file access (registered front end, unmerged buttons of a single file, writing a file back); the existing merged read is unchanged, so no changes are required in customer code.