Computational Logic Before 2026.1.0
Before ADITO 2026.1.0, row calculation did not use one consistent preparation strategy
across all Entity access paths. This mainly affected logic that processed several rows one
after another, such as table loading in the UI, entities.getRows, and Entity Webservices.
For customizing developers, this means that calculated values, display values, and dependency-based logic could behave differently depending on how the same Entity data was loaded.
Conclusion: When you maintain projects before ADITO 2026.1.0, do not assume that a calculation tested
in the UI behaves identically in entities.getRows or in an Entity Webservice. Validate
custom logic in the access path where it is used.
In a nutshell
jditoCalculationVersion:V1(deprecated) orV2(default)- different calculation mechanism for processing several rows on after another in:
- UI components – for example, a table
entities.getRows- Entity Webservice
- results in inconsistent behavior, different results and different runtime performance
Preferences jditoCalculationVersion
The jditoCalculationVersion is a setting, to configure the calculation mode.
V1existed for a long time in the system, but is already deprecated since years. It is worse thanV2in regard to determining and keeping dependencies, which result results in more calculations at which leads to performance issues.V2is the default mode and offers a more consistent approach to dependency regocnition. It is therefor more performant and allows better optimization on entity-level.
Because V1 is deprecated for so long and will be removed with future versions,
we will only cover the V2 behaviour here. Learn more about V2 in Understanding Variable Dependencies.
Multi-row Processing
Row Preparation During Multi-Row Processing
When several records are processed in sequence, ADITO internally prepares the row state before loading the next record. Before version 2026.1.0, different parts of the platform used different approaches for this preparation.
Figure: Simplified model of repeated row preparation and processing during multi-row loading.
An Example
The following table shows five rows that are loaded one after another. Before the first row is loaded, the calculation starts from an initial state that does not contain row values. Each loaded row then creates a new situation for the calculation engine.
| ID | Organisation Name | Turnover (Several rows) | State |
|---|---|---|---|
| a7f3c9e4-5b2d-4f8a-9c1e-3d7b8a6f4e2c | Global Tech Solutions | 2,450,000.00 | active |
| b8e4d0f5-6c3e-5g9b-0d2f-4e8c9b7g5f3d | Innovate Industries | 1,750,500.50 | active |
| c9f5e1g6-7d4f-6h0c-1e3g-5f9d0c8h6g4e | Fax Manufacturing and Repair | 1,230.75 | inactive |
| d0g6f2h7-8e5g-7i1d-2f4h-6g0e1d9i7h5f | NextGen Enterprises | 3,125,780.25 | active |
| e1h7i2j8-9f6g-8h7i-9j8k-7i9l0j1k2l3m | Global Tech Solutions | 2,450,000.00 | delete |
Example raw data for a table of five organizations.
Depending on the row processing approach, the calculation differs from one row to the next. Take a look at the different approaches in the following sections.
Overview
| Approach | Used when |
|---|---|
| Calculate once initially, only calculate differences afterward | reading rows with Entity Webservice |
| Do nothing, only calculate differences between rows | reading rows with entities.getRows |
| Reset field values to a fresh start for each row | reading rows within the UI, for example: tables, titled list, card view, etc. |
Approach: Calculate once initially, only calculate differences afterward
Behavior: No field reset was performed before the next row was loaded.
Impact: Calculated values start from null or from a virtual row state that did not exist in the RecordContainer.
Changes do trigger recalculations.
Overall better performance, though values may not be as expected.
Example: Looking at the example data above this leads to:
| ID | NAME | turnover | STATE | isActive |
|---|---|---|---|---|
| a7f3c9e4-5b2d-4f8a-9c1e-3d7b8a6f4e2c | Global Tech Solutions | 0 | active | true |
| b8e4d0f5-6c3e-5g9b-0d2f-4e8c9b7g5f3d | Innovate Industries | 0 | active | true |
| c9f5e1g6-7d4f-6h0c-1e3g-5f9d0c8h6g4e | Fax Manufacturing and Repair | 0 | inactive | false |
| d0g6f2h7-8e5g-7i1d-2f4h-6g0e1d9i7h5f | NextGen Enterprises | 0 | active | true |
| e1h7i2j8-9f6g-8h7i-9j8k-7i9l0j1k2l3m | Global Tech Solutions | 0 | delete | false |
Constraints for the turnover value:
- is calculated from other data in the system
- defaults to
0 - is only calculated as long
this.valueisnull
In our example, the first calculation basis is null for the virtual row. This leads to
a turnover value of 0 that is not calculated again.
Constraints for the isActive value:
- is calculated based on the state
- defaults to
false - does NOT check if
this.valueisnull
In our example, the first calculation basis is null for the virtual row. This leads to
an isActive value of false. Because the dependent value from the state changed, the
calculation is triggered again and leads to the expected value.
Approach: Do nothing, only calculate differences between rows
Behavior: No field reset is performed before the next row is loaded, and no initial calculation is performed before the first real row is loaded. Only differences between the currently loaded row and the previously loaded row triggered recalculations.
Impact: Calculated values could keep the value from the first row that calculated them. This was usually faster than resetting fields for every row, but calculations that depended on an implicit fresh start could produce stale values for later rows.
Example: Looking at the example data above this leads to:
| ID | NAME | turnover | STATE | isActive |
|---|---|---|---|---|
| a7f3c9e4-5b2d-4f8a-9c1e-3d7b8a6f4e2c | Global Tech Solutions | 2,450,000.00 | active | true |
| b8e4d0f5-6c3e-5g9b-0d2f-4e8c9b7g5f3d | Innovate Industries | 2,450,000.00 | active | true |
| c9f5e1g6-7d4f-6h0c-1e3g-5f9d0c8h6g4e | Fax Manufacturing and Repair | 2,450,000.00 | inactive | false |
| d0g6f2h7-8e5g-7i1d-2f4h-6g0e1d9i7h5f | NextGen Enterprises | 2,450,000.00 | active | true |
| e1h7i2j8-9f6g-8h7i-9j8k-7i9l0j1k2l3m | Global Tech Solutions | 2,450,000.00 | delete | false |
Constraints for the turnover value:
- is calculated from other data in the system
- defaults to
0 - is only calculated as long
this.valueisnull
In this approach, the first real row starts with a turnovers this.value as null.
The turnover is therefore calculated for the first organization. Because the field is not
reset afterward, this.value is no longer null for the following rows. The guarded
calculation is not executed again, so the later rows keep the turnover value from the
first row.
Constraints for the isActive value:
- is calculated based on the state
- defaults to
false - does NOT check if
this.valueisnull
In this approach, isActive is calculated for the first row and then recalculated whenever
the dependent state value changes between rows. The second row keeps true, because
the state remains active. The following rows recalculate the value when the state
changes to inactive, active, and delete.
Approach: Reset field values to a fresh start for each row
Behavior: Field values and display values are reset before each row is loaded.
Each row is then calculated from a fresh field state. This reset only affects value and displayValue,
but not other field processes such as dropDownProcess.
Impact: Calculated values did not keep state from the previously processed row. This matched the UI behavior, but it is significantly slower because every row requires a resource intensive recalculation.
Example: Looking at the example data above this leads to:
| ID | NAME | turnover | STATE | isActive |
|---|---|---|---|---|
| a7f3c9e4-5b2d-4f8a-9c1e-3d7b8a6f4e2c | Global Tech Solutions | 2,450,000.00 | active | true |
| b8e4d0f5-6c3e-5g9b-0d2f-4e8c9b7g5f3d | Innovate Industries | 1,750,500.50 | active | true |
| c9f5e1g6-7d4f-6h0c-1e3g-5f9d0c8h6g4e | Fax Manufacturing and Repair | 1,230.75 | inactive | false |
| d0g6f2h7-8e5g-7i1d-2f4h-6g0e1d9i7h5f | NextGen Enterprises | 3,125,780.25 | active | true |
| e1h7i2j8-9f6g-8h7i-9j8k-7i9l0j1k2l3m | Global Tech Solutions | 2,450,000.00 | delete | false |
Constraints for the turnover value:
- is calculated from other data in the system
- defaults to
0 - is only calculated as long
this.valueisnull
In this approach, every row starts with a turnovers this.value of null because the field
is reset before the row is processed. The turnover calculation is therefore executed for
each row and returns the row-specific value.
Constraints for the isActive value:
- is calculated based on the state
- defaults to
false - does NOT check if
this.valueisnull
In this approach, isActive is recalculated for each row from the freshly loaded state
value. Because the field state is reset before every row, the result does not depend on
the state of the previously processed row.
Consequences of this behavior
The most significant consequence is not only performance. The same Entity could produce different calculated results depending on whether the data was loaded in the UI, through Entity access methods, or through an Entity Webservice. It also makes customization harder because the result is different for different access paths. This can easily lead to bugs that are hard to track down as they only present under certain circumstances.
tl;dr – Why This Matters for Customizing within the UnifiedEntityModel
Custom logic is most vulnerable when it relies on implicit row state. Typical examples are:
valueProcessordisplayValueProcesslogic that reads other EntityFields whose values are calculated.onValueChangelogic that is triggered while rows are loaded or recalculated.- Calculations that assume an empty or freshly initialized field state for each row.
- Calculations that behave differently when the first calculation basis is
null.
In these cases, the reset strategy can change the result because the calculation may see different previous values, missing values, or recalculated values.
The issue is often easier to understand with Entities that contain several calculated
fields and dependencies. Organisation_entity is a useful example for checking this
behavior in legacy projects.
Recommended Practice for System running before 2026.1.0
For projects that stay on a version before 2026.1.0, treat row calculations as access-path-specific behavior:
- Test calculated values in the exact access path that is used in production, for example
UI table loading,
entities.getRows, or an Entity Webservice. - Avoid custom logic that depends on fields being reset implicitly before every row.
- Initialize values explicitly inside custom logic when the calculation requires a known starting value.
- Pay special attention to
valueProcessanddisplayValueProcessimplementations in Entities with many rows, because full recalculation can have a noticeable performance impact.
If a project is migrated to ADITO 2026.1.0 or later, review this logic together with the new row recalculation mode. See Computational Logic Starting With 2026.1.0.