Showing posts with label Dynamics AX. Show all posts
Showing posts with label Dynamics AX. Show all posts

Friday, 3 August 2018

FirstOnly in datasource and its alternative (using ROW_NUMBER())

When there’s a requirement to retrieve only the first record from datasource, we might think the “FirstOnly” property under the datasource table will comes in handy, but unfortunately it doesn’t.

I’ve created a View and set the datasource’s FirstOnly property to Yes, then created a class to select from this View, it still returns all the records.
Not sure how this property works, but that’s not the focus of this article.

Screenshot showing the “FirstOnly” property



As an alternative, we can create an additional field (RowNumber) and filter it using RowNumber = 1.

Eg. Given the sample tables below, the requirement is to shows only one contact number for the customer.

Customer
Contact
AccountNum
AccountNum
Locator
ABC
ABC
12345
DEF
ABC
34567
DEF
56789
DEF
78901
DEF
90123

We’ll start with creating the View method under the View.

public class QTQ_CustNonPrimaryPhoneView extends common
{
    public static server str rowNumByAccountNum()
    {
        return 'ROW_NUMBER() OVER (PARTITION BY T1.Partition, T1.AccountNum ORDER BY AccountNum)';
    }
}

Then create a String computed column, set the View Method with this method name.



Select from this View will product result as shown below.

AccountNum
Locator
RowNumByAccountNum
ABC
12345
1
ABC
34567
2
DEF
56789
1
DEF
78901
2
DEF
90123
3

You can easily filter it with “RowNumByAccountNum = 1” to retrieve only the first contact.

Eg.
When using it in code
select from QTQ_CustNonPrimaryPhoneView where QTQ_CustNonPrimaryPhoneView.RowNumByAccountNum = 1.

When using it in Query/View, set the Range by putting in the field ‘RowNumByAccountNum’ and set the value to 1



Tuesday, 6 March 2018

Error - You are not authorized to access table '' (). Contact your system administrator.

When getting this error "You are not authorized to access table '' (). Contact your system administrator.", two of the possible reasons would be:
  1. The configuration key for the table is not enabled (Check it under System administration > Setup > Licensing > License configuration)
     
  2. The View does not have any field (Expand the "Field" node under the View to check it)

Friday, 14 July 2017

DIXF - System.Security.Cryptography.CryptographicException: Key not valid for use in specified state

Had this error each time we replaced the business database in our Test environment.

It occur at the Data Import Export Framework > Setup > Source data format > Validate (button).

Took a quick look at the code which causes this error, it came from \Data Dictionary\Tables\DMFDataSourceProperties\Methods\validateConnectionString - Ln 49, on the statement "cryptoblob2str(WinAPIServer::cryptUnProtectData(_dmfDataSourceProperties.Password))".

It is trying to decrypt data that's encrypted from another environment, which will cause error in this case. The "Password" field in the table is storing the encrypted data. Our setup is to use "System DSN " from the "AOS", so there're two way for us to fix it:
1. Clear the "Password" field from back-end (Eg. DMFDataSourceProperties.Password = conNull(); DMFDataSourceProperties.update();)
2. Delete the existing record and create a new one with the exact same setup

It works in our case due to we just need this field to be empty as it won't be used.


Detail error message
System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Security.Cryptography.CryptographicException: Key not valid for use in specified state.
   at System.Security.Cryptography.ProtectedData.Unprotect(Byte[] encryptedData, Byte[] optionalEntropy, DataProtectionScope scope)
   --- End of inner exception stack trace ---
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

   at Microsoft.Dynamics.AX.ManagedInterop.ClrBridgeCore.InvokeClrStaticMethod(Char* pszClassName, Char* pszMethodName, Char* assemblyName, Int32 argsLength, ObjectWrapper** arguments, Boolean* argsAreByRef, Boolean* isException)

Sunday, 9 July 2017

Error - The field 'FieldName' on the extension 'ExtensionName.Extension' already exists. This extension cannot be applied to the base element.

In a recent D365O development, I came across this error when moving fields from a model to another model.

The field 'FieldName' on the extension 'ExtensionName.Extension' already exists. This extension cannot be applied to the base element.

The scenario is that while doing testing on development, some fields have been moved to the standard element (overlayered it by "Customize") [Eg. This is in ModelA]. When the test is done, these field are then moved back to the extension [Eg. This is in ModelB]. Unfortunately after moved it back to the extension, this error occur during the build of the solution.

This is what has been derived:
  • When the fields are moved to the base element, a build is done and the assembly is updated
  • After the testing is done, the change on the base element is deleted (deleting the whole overlayered element)
  • Then used the "Get latest" to restore the extension (it is where the fields are originally created)
  • Then build solution again and error occur
  • It is suspected that after the change in base element is deleted, the reference to the assembly still there. Hence, when building the solution of the extension, it found that the same fields already exist in the base element.

Solution
  1. In Visual Studio, go to Dynamics 365 > Build models
  2. Select only the model where the base element sit (Eg. Application suite)
  3. Build it
  4. Then build the solution again

Tuesday, 21 February 2017

Performance improvement on sp_WHSOnHand / sp_WHSOnHandWithDelta

These two stored procedure are generated by AX during synchronization (there're others as well, but not the focus of this post).
- sp_WHSOnHand
- sp_WHSOnHandWithDelta

In our environment (with certain amount of data), it tooks around 250ms to execute, although this number looks small, but when you have 100 of them being executed, that's 25,000ms (25 seconds), it is a different story then. A wait of 25s isn't something nice for the user.

Analyzing the query plan shows that it starts from InventDim, getting a large number of records and then nested loop to WHSInventReserve and so on. Looking at the thickness of the arrow next to InventDim, that's an indicator that a large number of rows are returned from that operator. That means it is operating that large number of rows against the WHSInventReserve.

This is the major bottleneck of the query.
We've tried update statistics and reindex and clear the cache to make sure it generate a new plan, but it still generate the same execution plan.

Due to this stored procedure has an ItemId provided, it should limit the number of rows from the WHSInventReserve, using this limited number of rows and nested loop against the InventDim would be much quicker, but somehow the optimizer didn't think so.

Bad query plan

To resolve this problem, I'm splitting this query into two parts.
First, filter the WHSInventReserve with the ItemId provided and insert it into a table variable.
Then join this table variable to the InventDim. Although the sum of the estimated subtree cost of the original query and the new 2-parts query is similar, but it runs much quicker than the original version.

Better query plan

With this new change, the stored procedure completed in 7ms instead of 250ms.

Original query join

New query join

The stored procedure should be changed from within AX at the 'WHSOnHandSPHelper' class. When synchronization is run, it regenerate this stored procedure. It also regenerate when stock dimension setup is changed.

Below is a sample of complete stored procedure code generated by the 'WHSOnHandSPHelper' class (Do take note, the selection field changed based on the stock dimension setup).

=================================================
CREATE PROCEDURE [dbo].[sp_WHSOnHand]
    @PARTITION bigint,
    @LEVEL int,
    @DATAAREAID nvarchar (4),
    @ITEMID nvarchar (20),
    @INVENTSITEID nvarchar (10) = NULL,
    @INVENTSITEIDLEVEL int = 1024,
    @INVENTLOCATIONID nvarchar (10) = NULL,
    @INVENTLOCATIONIDLEVEL int = 1024,
    @WMSLOCATIONID nvarchar (10) = NULL,
    @WMSLOCATIONIDLEVEL int = 1024,
    @WMSPALLETID nvarchar (18) = NULL,
    @WMSPALLETIDLEVEL int = 1024,
    @INVENTSTATUSID nvarchar (10) = NULL,
    @INVENTSTATUSIDLEVEL int = 1024,
    @LICENSEPLATEID nvarchar (25) = NULL,
    @LICENSEPLATEIDLEVEL int = 1024,
    @INVENTBATCHID nvarchar (20) = NULL,
    @INVENTBATCHIDLEVEL int = 1024,
    @INVENTSERIALID nvarchar (20) = NULL,
    @INVENTSERIALIDLEVEL int = 1024,
    @INVENTPROFILEID_RU nvarchar (10) = NULL,
    @INVENTPROFILEID_RULEVEL int = 1024,
    @INVENTOWNERID_RU nvarchar (20) = NULL,
    @INVENTOWNERID_RULEVEL int = 1024,
    @INVENTGTDID_RU nvarchar (30) = NULL,
    @INVENTGTDID_RULEVEL int = 1024,
    @CONFIGID nvarchar (10) = NULL,
    @INVENTSIZEID nvarchar (10) = NULL,
    @INVENTCOLORID nvarchar (10) = NULL,
    @INVENTSTYLEID nvarchar (10) = NULL,
    -- Output parameters
    @AVAILPHYSICAL  numeric(32, 16) OUTPUT,
    @AVAILORDERED  numeric(32, 16) OUTPUT
AS

-- Without nocount on the procedure will return number of affected rows in the result set which is
-- harmful to performance and requires us to adjust field retrieval when calling the stored procedure
SET NOCOUNT ON;

-- We need to supress warnings when trying to aggregate NULL columns. This is a valid business scenario
-- and happens e.g. for a non-LP controlled location with batch below item.
SET ANSI_WARNINGS OFF;

-- The @RESULT table variable is used to store intermediate quantities.
DECLARE @RESULT TABLE(
    AVAILPHYSICALRESULT numeric(32, 16) NOT NULL, -- Aggregated minimum qtys from lower level in @RESULT
    AVAILORDEREDRESULT numeric(32, 16) NOT NULL, -- Aggregated minimum qtys from lower level in @RESULT
    AVAILPHYSICALFOUND numeric(32, 16), -- Aggregated qty for current level in WHSInventReserve
    AVAILORDEREDFOUND numeric(32, 16), -- Aggregated qty for current level in WHSInventReserve
    HIERARCHYLEVEL int NOT NULL,
    CONFIGID nvarchar (10) NULL,
    INVENTSIZEID nvarchar (10) NULL,
    INVENTCOLORID nvarchar (10) NULL,
    INVENTSTYLEID nvarchar (10) NULL,
    INVENTSITEID nvarchar (10) NULL,
    INVENTLOCATIONID nvarchar (10) NULL,
    WMSLOCATIONID nvarchar (10) NULL,
    WMSPALLETID nvarchar (18) NULL,
    INVENTSTATUSID nvarchar (10) NULL,
    LICENSEPLATEID nvarchar (25) NULL,
    INVENTBATCHID nvarchar (20) NULL,
    INVENTSERIALID nvarchar (20) NULL,
    INVENTPROFILEID_RU nvarchar (10) NULL,
    INVENTOWNERID_RU nvarchar (20) NULL,
    INVENTGTDID_RU nvarchar (30) NULL);

DECLARE @DIMLIST TABLE(
    INVENTDIMID nvarchar (20) NULL,
    AVAILPHYSICAL numeric(32, 16) NOT NULL,
    AVAILORDERED numeric(32, 16) NOT NULL);

-- The algorithm of the stored procedure is as follows:
-- 1. Initialize the @RESULT table with aggregated qtys in WHSInventReserve for the level requested by user
-- 2. Traverse the hierarchy up and for each level:
--     1) Aggregate qtys from previous level in @RESULT grouping by dimensions of current hierarchy level
--     2) Aggregate qtys from current level in WHSInventReserve grouping by dimensions of current hierarchy level
--     3) Select the minimum of the above qtys and insert into @RESULT
-- 3. When hierarchy level 0 is reached, return the sum of aggregated qtys in @RESULT

-- In the original query (the one after below query), it is joining InventDim and WHSInventReserve and filter records based on some criteria.
-- But in the execution plan, it doesn't seems to generate a good plan (even after UPDATE STATISTICS). In the original execution plan, it always
-- Get records from InventDim first, which returns lot of rows, then only join it to WHSInventReserve and continue filter from there.
-- If we makes it get records from WHSInventReserve first, filter it, then with lesser rows from here, join it to InventDim, it will be quicker.
-- So the optimization is to filter WHSInventResreve first, then use this result at the original query. In the test, this has reduce the execution
-- time from ~250ms to ~7ms
  INSERT INTO @DIMLIST(INVENTDIMID, AVAILPHYSICAL, AVAILORDERED)
  SELECT INVENTDIMID, AVAILPHYSICAL, AVAILORDERED
    FROM WHSINVENTRESERVE WHSINVENTRESERVE
   WHERE WHSINVENTRESERVE.ITEMID         = @ITEMID
     AND WHSINVENTRESERVE.PARTITION      = @PARTITION
     AND WHSINVENTRESERVE.DATAAREAID     = @DATAAREAID
     AND WHSINVENTRESERVE.HIERARCHYLEVEL = @LEVEL

-- Initialize the @RESULT variable.
-- Loads aggregated data for the lowest level requested by user.
INSERT INTO @RESULT (AVAILPHYSICALRESULT, AVAILORDEREDRESULT, AVAILPHYSICALFOUND, AVAILORDEREDFOUND, HIERARCHYLEVEL, CONFIGID, INVENTSIZEID, INVENTCOLORID, INVENTSTYLEID, INVENTSITEID, INVENTLOCATIONID, WMSLOCATIONID, WMSPALLETID, INVENTSTATUSID, LICENSEPLATEID, INVENTBATCHID, INVENTSERIALID, INVENTPROFILEID_RU, INVENTOWNERID_RU, INVENTGTDID_RU)
SELECT SUM(DIMLIST.AVAILPHYSICAL),
    SUM(DIMLIST.AVAILORDERED),
    NULL,
    NULL,
    @LEVEL,
    INVENTDIM.CONFIGID,
    INVENTDIM.INVENTSIZEID,
    INVENTDIM.INVENTCOLORID,
    INVENTDIM.INVENTSTYLEID,
    CASE WHEN @LEVEL + -1 >= @INVENTSITEIDLEVEL THEN INVENTDIM.INVENTSITEID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTLOCATIONIDLEVEL THEN INVENTDIM.INVENTLOCATIONID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @WMSLOCATIONIDLEVEL THEN INVENTDIM.WMSLOCATIONID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @WMSPALLETIDLEVEL THEN INVENTDIM.WMSPALLETID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTSTATUSIDLEVEL THEN INVENTDIM.INVENTSTATUSID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @LICENSEPLATEIDLEVEL THEN INVENTDIM.LICENSEPLATEID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTBATCHIDLEVEL THEN INVENTDIM.INVENTBATCHID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTSERIALIDLEVEL THEN INVENTDIM.INVENTSERIALID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTPROFILEID_RULEVEL THEN INVENTDIM.INVENTPROFILEID_RU ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTOWNERID_RULEVEL THEN INVENTDIM.INVENTOWNERID_RU ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTGTDID_RULEVEL THEN INVENTDIM.INVENTGTDID_RU ELSE NULL END
    FROM INVENTDIM INVENTDIM
    JOIN @DIMLIST DIMLIST
      ON INVENTDIM.INVENTDIMID = DIMLIST.INVENTDIMID
    WHERE   INVENTDIM.DATAAREAID = @DATAAREAID
        AND INVENTDIM.PARTITION = @PARTITION
        AND (@CONFIGID IS NULL OR INVENTDIM.CONFIGID = @CONFIGID)
        AND (@INVENTSIZEID IS NULL OR INVENTDIM.INVENTSIZEID = @INVENTSIZEID)
        AND (@INVENTCOLORID IS NULL OR INVENTDIM.INVENTCOLORID = @INVENTCOLORID)
        AND (@INVENTSTYLEID IS NULL OR INVENTDIM.INVENTSTYLEID = @INVENTSTYLEID)
        AND (@LEVEL < @INVENTSITEIDLEVEL OR @INVENTSITEID IS NULL OR @INVENTSITEID = INVENTDIM.INVENTSITEID)
        AND (@LEVEL < @INVENTLOCATIONIDLEVEL OR @INVENTLOCATIONID IS NULL OR @INVENTLOCATIONID = INVENTDIM.INVENTLOCATIONID)
        AND (@LEVEL < @WMSLOCATIONIDLEVEL OR @WMSLOCATIONID IS NULL OR @WMSLOCATIONID = INVENTDIM.WMSLOCATIONID)
        AND (@LEVEL < @WMSPALLETIDLEVEL OR @WMSPALLETID IS NULL OR @WMSPALLETID = INVENTDIM.WMSPALLETID)
        AND (@LEVEL < @INVENTSTATUSIDLEVEL OR @INVENTSTATUSID IS NULL OR @INVENTSTATUSID = INVENTDIM.INVENTSTATUSID)
        AND (@LEVEL < @LICENSEPLATEIDLEVEL OR @LICENSEPLATEID IS NULL OR @LICENSEPLATEID = INVENTDIM.LICENSEPLATEID)
        AND (@LEVEL < @INVENTBATCHIDLEVEL OR @INVENTBATCHID IS NULL OR @INVENTBATCHID = INVENTDIM.INVENTBATCHID)
        AND (@LEVEL < @INVENTSERIALIDLEVEL OR @INVENTSERIALID IS NULL OR @INVENTSERIALID = INVENTDIM.INVENTSERIALID)
        AND (@LEVEL < @INVENTPROFILEID_RULEVEL OR @INVENTPROFILEID_RU IS NULL OR @INVENTPROFILEID_RU = INVENTDIM.INVENTPROFILEID_RU)
        AND (@LEVEL < @INVENTOWNERID_RULEVEL OR @INVENTOWNERID_RU IS NULL OR @INVENTOWNERID_RU = INVENTDIM.INVENTOWNERID_RU)
        AND (@LEVEL < @INVENTGTDID_RULEVEL OR @INVENTGTDID_RU IS NULL OR @INVENTGTDID_RU = INVENTDIM.INVENTGTDID_RU)
GROUP BY
    INVENTDIM.CONFIGID,
    INVENTDIM.INVENTSIZEID,
    INVENTDIM.INVENTCOLORID,
    INVENTDIM.INVENTSTYLEID,
    CASE WHEN @LEVEL + -1 >= @INVENTSITEIDLEVEL THEN INVENTDIM.INVENTSITEID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTLOCATIONIDLEVEL THEN INVENTDIM.INVENTLOCATIONID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @WMSLOCATIONIDLEVEL THEN INVENTDIM.WMSLOCATIONID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @WMSPALLETIDLEVEL THEN INVENTDIM.WMSPALLETID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTSTATUSIDLEVEL THEN INVENTDIM.INVENTSTATUSID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @LICENSEPLATEIDLEVEL THEN INVENTDIM.LICENSEPLATEID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTBATCHIDLEVEL THEN INVENTDIM.INVENTBATCHID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTSERIALIDLEVEL THEN INVENTDIM.INVENTSERIALID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTPROFILEID_RULEVEL THEN INVENTDIM.INVENTPROFILEID_RU ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTOWNERID_RULEVEL THEN INVENTDIM.INVENTOWNERID_RU ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTGTDID_RULEVEL THEN INVENTDIM.INVENTGTDID_RU ELSE NULL END;

WHILE (@LEVEL > 0)
BEGIN

  DELETE FROM @DIMLIST

  INSERT INTO @DIMLIST(INVENTDIMID, AVAILPHYSICAL, AVAILORDERED)
  SELECT INVENTDIMID, AVAILPHYSICAL, AVAILORDERED
    FROM WHSINVENTRESERVE WHSINVENTRESERVE
   WHERE WHSINVENTRESERVE.HIERARCHYLEVEL = @LEVEL + -1
     AND WHSINVENTRESERVE.ITEMID         = @ITEMID
     AND WHSINVENTRESERVE.PARTITION      = @PARTITION
     AND WHSINVENTRESERVE.DATAAREAID     = @DATAAREAID

    INSERT INTO @RESULT (AVAILPHYSICALRESULT, AVAILORDEREDRESULT, AVAILPHYSICALFOUND, AVAILORDEREDFOUND, HIERARCHYLEVEL, CONFIGID, INVENTSIZEID, INVENTCOLORID, INVENTSTYLEID, INVENTSITEID, INVENTLOCATIONID, WMSLOCATIONID, WMSPALLETID, INVENTSTATUSID, LICENSEPLATEID, INVENTBATCHID, INVENTSERIALID, INVENTPROFILEID_RU, INVENTOWNERID_RU, INVENTGTDID_RU)
SELECT SUM(CASE WHEN RESULT.AVAILPHYSICALFOUND < RESULT.AVAILPHYSICALRESULT THEN RESULT.AVAILPHYSICALFOUND ELSE RESULT.AVAILPHYSICALRESULT END),
    SUM(CASE WHEN RESULT.AVAILORDEREDFOUND < RESULT.AVAILORDEREDRESULT THEN RESULT.AVAILORDEREDFOUND ELSE RESULT.AVAILORDEREDRESULT END),
    SUM(FOUND.AVAILPHYSICAL),
    SUM(FOUND.AVAILORDERED),
    @LEVEL + -1,
    RESULT.CONFIGID AS CONFIGID,
    RESULT.INVENTSIZEID AS INVENTSIZEID,
    RESULT.INVENTCOLORID AS INVENTCOLORID,
    RESULT.INVENTSTYLEID AS INVENTSTYLEID,
    CASE WHEN @LEVEL + -1 >= @INVENTSITEIDLEVEL THEN RESULT.INVENTSITEID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTLOCATIONIDLEVEL THEN RESULT.INVENTLOCATIONID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @WMSLOCATIONIDLEVEL THEN RESULT.WMSLOCATIONID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @WMSPALLETIDLEVEL THEN RESULT.WMSPALLETID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTSTATUSIDLEVEL THEN RESULT.INVENTSTATUSID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @LICENSEPLATEIDLEVEL THEN RESULT.LICENSEPLATEID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTBATCHIDLEVEL THEN RESULT.INVENTBATCHID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTSERIALIDLEVEL THEN RESULT.INVENTSERIALID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTPROFILEID_RULEVEL THEN RESULT.INVENTPROFILEID_RU ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTOWNERID_RULEVEL THEN RESULT.INVENTOWNERID_RU ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTGTDID_RULEVEL THEN RESULT.INVENTGTDID_RU ELSE NULL END
FROM @RESULT RESULT
LEFT OUTER JOIN (
SELECT SUM(DIMLIST.AVAILPHYSICAL) AS AVAILPHYSICAL,
    SUM(DIMLIST.AVAILORDERED) AS AVAILORDERED,
    INVENTDIM.CONFIGID AS CONFIGID,
    INVENTDIM.INVENTSIZEID AS INVENTSIZEID,
    INVENTDIM.INVENTCOLORID AS INVENTCOLORID,
    INVENTDIM.INVENTSTYLEID AS INVENTSTYLEID,
    CASE WHEN @LEVEL + -1 >= @INVENTSITEIDLEVEL THEN INVENTDIM.INVENTSITEID ELSE NULL END AS INVENTSITEID,
    CASE WHEN @LEVEL + -1 >= @INVENTLOCATIONIDLEVEL THEN INVENTDIM.INVENTLOCATIONID ELSE NULL END AS INVENTLOCATIONID,
    CASE WHEN @LEVEL + -1 >= @WMSLOCATIONIDLEVEL THEN INVENTDIM.WMSLOCATIONID ELSE NULL END AS WMSLOCATIONID,
    CASE WHEN @LEVEL + -1 >= @WMSPALLETIDLEVEL THEN INVENTDIM.WMSPALLETID ELSE NULL END AS WMSPALLETID,
    CASE WHEN @LEVEL + -1 >= @INVENTSTATUSIDLEVEL THEN INVENTDIM.INVENTSTATUSID ELSE NULL END AS INVENTSTATUSID,
    CASE WHEN @LEVEL + -1 >= @LICENSEPLATEIDLEVEL THEN INVENTDIM.LICENSEPLATEID ELSE NULL END AS LICENSEPLATEID,
    CASE WHEN @LEVEL + -1 >= @INVENTBATCHIDLEVEL THEN INVENTDIM.INVENTBATCHID ELSE NULL END AS INVENTBATCHID,
    CASE WHEN @LEVEL + -1 >= @INVENTSERIALIDLEVEL THEN INVENTDIM.INVENTSERIALID ELSE NULL END AS INVENTSERIALID,
    CASE WHEN @LEVEL + -1 >= @INVENTPROFILEID_RULEVEL THEN INVENTDIM.INVENTPROFILEID_RU ELSE NULL END AS INVENTPROFILEID_RU,
    CASE WHEN @LEVEL + -1 >= @INVENTOWNERID_RULEVEL THEN INVENTDIM.INVENTOWNERID_RU ELSE NULL END AS INVENTOWNERID_RU,
    CASE WHEN @LEVEL + -1 >= @INVENTGTDID_RULEVEL THEN INVENTDIM.INVENTGTDID_RU ELSE NULL END AS INVENTGTDID_RU
FROM INVENTDIM INVENTDIM
JOIN @DIMLIST DIMLIST
     ON INVENTDIM.PARTITION = @PARTITION
    AND INVENTDIM.DATAAREAID  = @DATAAREAID
    AND INVENTDIM.INVENTDIMID = DIMLIST.INVENTDIMID
GROUP BY
    INVENTDIM.CONFIGID,
    INVENTDIM.INVENTSIZEID,
    INVENTDIM.INVENTCOLORID,
    INVENTDIM.INVENTSTYLEID,
    CASE WHEN @LEVEL + -1 >= @INVENTSITEIDLEVEL THEN INVENTDIM.INVENTSITEID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTLOCATIONIDLEVEL THEN INVENTDIM.INVENTLOCATIONID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @WMSLOCATIONIDLEVEL THEN INVENTDIM.WMSLOCATIONID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @WMSPALLETIDLEVEL THEN INVENTDIM.WMSPALLETID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTSTATUSIDLEVEL THEN INVENTDIM.INVENTSTATUSID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @LICENSEPLATEIDLEVEL THEN INVENTDIM.LICENSEPLATEID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTBATCHIDLEVEL THEN INVENTDIM.INVENTBATCHID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTSERIALIDLEVEL THEN INVENTDIM.INVENTSERIALID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTPROFILEID_RULEVEL THEN INVENTDIM.INVENTPROFILEID_RU ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTOWNERID_RULEVEL THEN INVENTDIM.INVENTOWNERID_RU ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTGTDID_RULEVEL THEN INVENTDIM.INVENTGTDID_RU ELSE NULL END
    ) AS FOUND ON (RESULT.CONFIGID = FOUND.CONFIGID)
        AND (RESULT.INVENTSIZEID = FOUND.INVENTSIZEID)
        AND (RESULT.INVENTCOLORID = FOUND.INVENTCOLORID)
        AND (RESULT.INVENTSTYLEID = FOUND.INVENTSTYLEID)
        AND (@LEVEL + -1 < @INVENTSITEIDLEVEL OR RESULT.INVENTSITEID = FOUND.INVENTSITEID)
        AND (@LEVEL + -1 < @INVENTLOCATIONIDLEVEL OR RESULT.INVENTLOCATIONID = FOUND.INVENTLOCATIONID)
        AND (@LEVEL + -1 < @WMSLOCATIONIDLEVEL OR RESULT.WMSLOCATIONID = FOUND.WMSLOCATIONID)
        AND (@LEVEL + -1 < @WMSPALLETIDLEVEL OR RESULT.WMSPALLETID = FOUND.WMSPALLETID)
        AND (@LEVEL + -1 < @INVENTSTATUSIDLEVEL OR RESULT.INVENTSTATUSID = FOUND.INVENTSTATUSID)
        AND (@LEVEL + -1 < @LICENSEPLATEIDLEVEL OR RESULT.LICENSEPLATEID = FOUND.LICENSEPLATEID)
        AND (@LEVEL + -1 < @INVENTBATCHIDLEVEL OR RESULT.INVENTBATCHID = FOUND.INVENTBATCHID)
        AND (@LEVEL + -1 < @INVENTSERIALIDLEVEL OR RESULT.INVENTSERIALID = FOUND.INVENTSERIALID)
        AND (@LEVEL + -1 < @INVENTPROFILEID_RULEVEL OR RESULT.INVENTPROFILEID_RU = FOUND.INVENTPROFILEID_RU)
        AND (@LEVEL + -1 < @INVENTOWNERID_RULEVEL OR RESULT.INVENTOWNERID_RU = FOUND.INVENTOWNERID_RU)
        AND (@LEVEL + -1 < @INVENTGTDID_RULEVEL OR RESULT.INVENTGTDID_RU = FOUND.INVENTGTDID_RU)
WHERE RESULT.HIERARCHYLEVEL = @LEVEL
GROUP BY
    RESULT.CONFIGID,
    RESULT.INVENTSIZEID,
    RESULT.INVENTCOLORID,
    RESULT.INVENTSTYLEID,
    CASE WHEN @LEVEL + -1 >= @INVENTSITEIDLEVEL THEN RESULT.INVENTSITEID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTLOCATIONIDLEVEL THEN RESULT.INVENTLOCATIONID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @WMSLOCATIONIDLEVEL THEN RESULT.WMSLOCATIONID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @WMSPALLETIDLEVEL THEN RESULT.WMSPALLETID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTSTATUSIDLEVEL THEN RESULT.INVENTSTATUSID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @LICENSEPLATEIDLEVEL THEN RESULT.LICENSEPLATEID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTBATCHIDLEVEL THEN RESULT.INVENTBATCHID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTSERIALIDLEVEL THEN RESULT.INVENTSERIALID ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTPROFILEID_RULEVEL THEN RESULT.INVENTPROFILEID_RU ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTOWNERID_RULEVEL THEN RESULT.INVENTOWNERID_RU ELSE NULL END,
    CASE WHEN @LEVEL + -1 >= @INVENTGTDID_RULEVEL THEN RESULT.INVENTGTDID_RU ELSE NULL END;


    SET @LEVEL = @LEVEL - 1
END; -- WHILE

SELECT @AVAILPHYSICAL = SUM(CASE WHEN AVAILPHYSICALFOUND < AVAILPHYSICALRESULT THEN AVAILPHYSICALFOUND ELSE AVAILPHYSICALRESULT END),
    @AVAILORDERED = SUM(CASE WHEN AVAILORDEREDFOUND < AVAILORDEREDRESULT THEN AVAILORDEREDFOUND ELSE AVAILORDEREDRESULT END)
FROM @RESULT
WHERE HIERARCHYLEVEL = 0;

GO
=================================================

Below is the XPO extract of the updated 'WHSOnHandSPHelper' class
=================================================
Exportfile for AOT version 1.0 or later
Formatversion: 1

***Element: CLS

; Microsoft Dynamics AX Class: WHSOnHandSPHelper unloaded
; --------------------------------------------------------------------------------
  CLSVERSION 1
  
  CLASS #WHSOnHandSPHelper
    PROPERTIES
      Name                #WHSOnHandSPHelper
      Origin              #{E0CDDBB5-8AE5-4BEF-9B5B-A41A300F36E5}
    ENDPROPERTIES
    
    METHODS
      SOURCE #buildCreateSPStmt
        #private str buildCreateSPStmt(boolean _includeDelta)
        #{
        #    str             ret = '';
        #    ListEnumerator  enabledDimsEnumerator;
        #    ListEnumerator  productDimensionsEnumerator;
        #    FieldId         dimFieldId;
        #    str             dimFieldName;
        #    boolean         isSerialEnabled;
        #
        #    if(useOptimizedVersion)
        #    {
        #        ret = this.TEC_buildCreateSPStmt(_includeDelta);
        #    }
        #    else
        #    {
        #        InventDimMetaDataCache::clearCache(); //in some cases the cache is not updated before sync making below SP
        #
        #        enabledDimsEnumerator = this.enabledDimensions().getEnumerator();
        #        productDimensionsEnumerator = this.productDimensions().getEnumerator();
        #        isSerialEnabled = this.getEnabledDimsParametersMap().exists(fieldNum(InventDim, InventSerialId));
        #
        #        /*
        #CREATE PROCEDURE [sp_WHSOnHand]
        #    @PARTITION bigint,
        #    @LEVEL int,
        #    @DATAAREAID nvarchar (4),
        #    @ITEMID nvarchar (20),
        #    @INVENTSITEID nvarchar (10) = NULL,
        #    @INVENTSITEIDLEVEL int = 1024,
        #    @INVENTLOCATIONID nvarchar (10) = NULL,
        #    @INVENTLOCATIONIDLEVEL int = 1024,
        #    @WMSLOCATIONID nvarchar (10) = NULL,
        #    @WMSLOCATIONIDLEVEL int = 1024,
        #    @WMSPALLETID nvarchar (18) = NULL,
        #    @WMSPALLETIDLEVEL int = 1024,
        #    @INVENTSTATUSID nvarchar (10) = NULL,
        #    @INVENTSTATUSIDLEVEL int = 1024,
        #    @LICENSEPLATEID nvarchar (25) = NULL,
        #    @LICENSEPLATEIDLEVEL int = 1024,
        #    @INVENTBATCHID nvarchar (20) = NULL,
        #    @INVENTBATCHIDLEVEL int = 1024,
        #    @INVENTSERIALID nvarchar (20) = NULL,
        #    @INVENTSERIALIDLEVEL int = 1024,
        #    @INVENTPROFILEID_RU nvarchar (10) = NULL,
        #    @INVENTPROFILEID_RULEVEL int = 1024,
        #    @INVENTOWNERID_RU nvarchar (20) = NULL,
        #    @INVENTOWNERID_RULEVEL int = 1024,
        #    @INVENTGTDID_RU nvarchar (30) = NULL,
        #    @INVENTGTDID_RULEVEL int = 1024,
        #    @CONFIGID nvarchar (10) = NULL,
        #    @INVENTSIZEID nvarchar (10) = NULL,
        #    @INVENTCOLORID nvarchar (10) = NULL,
        #    @INVENTSTYLEID nvarchar (10) = NULL,
        #    -- Output parameters
        #    @AVAILPHYSICAL  numeric(32, 16) OUTPUT,
        #    @AVAILORDERED  numeric(32, 16) OUTPUT
        #AS
        #
        #-- Without nocount on the procedure will return number of affected rows in the result set which is
        #-- harmful to performance and requires us to adjust field retrieval when calling the stored procedure
        #SET NOCOUNT ON;
        #
        #-- We need to supress warnings when trying to aggregate NULL columns. This is a valid business scenario
        #-- and happens e.g. for a non-LP controlled location with batch below item.
        #SET ANSI_WARNINGS OFF;
        #
        #-- The @RESULT table variable is used to store intermediate quantities.
        #DECLARE @RESULT TABLE(
        #    AVAILPHYSICALRESULT numeric(32, 16) NOT NULL, -- Aggregated minimum qtys from lower level in @RESULT
        #    AVAILORDEREDRESULT numeric(32, 16) NOT NULL, -- Aggregated minimum qtys from lower level in @RESULT
        #    AVAILPHYSICALFOUND numeric(32, 16), -- Aggregated qty for current level in WHSInventReserve
        #    AVAILORDEREDFOUND numeric(32, 16), -- Aggregated qty for current level in WHSInventReserve
        #    HIERARCHYLEVEL int NOT NULL,
        #    CONFIGID nvarchar (10) NULL,
        #    INVENTSIZEID nvarchar (10) NULL,
        #    INVENTCOLORID nvarchar (10) NULL,
        #    INVENTSTYLEID nvarchar (10) NULL,
        #    INVENTSITEID nvarchar (10) NULL,
        #    INVENTLOCATIONID nvarchar (10) NULL,
        #    WMSLOCATIONID nvarchar (10) NULL,
        #    WMSPALLETID nvarchar (18) NULL,
        #    INVENTSTATUSID nvarchar (10) NULL,
        #    LICENSEPLATEID nvarchar (25) NULL,
        #    INVENTBATCHID nvarchar (20) NULL,
        #    INVENTSERIALID nvarchar (20) NULL,
        #    INVENTPROFILEID_RU nvarchar (10) NULL,
        #    INVENTOWNERID_RU nvarchar (20) NULL,
        #    INVENTGTDID_RU nvarchar (30) NULL);
        #
        #-- The algorithm of the stored procedure is as follows:
        #-- 1. Initialize the @RESULT table with aggregated qtys in WHSInventReserve for the level requested by user
        #-- 2. Traverse the hierarchy up and for each level:
        #--     1) Aggregate qtys from previous level in @RESULT grouping by dimensions of current hierarchy level
        #--     2) Aggregate qtys from current level in WHSInventReserve grouping by dimensions of current hierarchy level
        #--     3) Select the minimum of the above qtys and insert into @RESULT
        #-- 3. When hierarchy level 0 is reached, return the sum of aggregated qtys in @RESULT
        #
        #-- Initialize the @RESULT variable.
        #-- Loads aggregated data for the lowest level requested by user.
        #INSERT INTO @RESULT (AVAILPHYSICALRESULT, AVAILORDEREDRESULT, AVAILPHYSICALFOUND, AVAILORDEREDFOUND, HIERARCHYLEVEL, CONFIGID, INVENTSIZEID, INVENTCOLORID, INVENTSTYLEID, INVENTSITEID, INVENTLOCATIONID, WMSLOCATIONID, WMSPALLETID, INVENTSTATUSID, LICENSEPLATEID, INVENTBATCHID, INVENTSERIALID, INVENTPROFILEID_RU, INVENTOWNERID_RU, INVENTGTDID_RU)
        #SELECT SUM(WHSINVENTRESERVE.AVAILPHYSICAL),
        #    SUM(WHSINVENTRESERVE.AVAILORDERED),
        #    NULL,
        #    NULL,
        #    @LEVEL,
        #    INVENTDIM.CONFIGID,
        #    INVENTDIM.INVENTSIZEID,
        #    INVENTDIM.INVENTCOLORID,
        #    INVENTDIM.INVENTSTYLEID,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSITEIDLEVEL THEN INVENTDIM.INVENTSITEID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTLOCATIONIDLEVEL THEN INVENTDIM.INVENTLOCATIONID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @WMSLOCATIONIDLEVEL THEN INVENTDIM.WMSLOCATIONID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @WMSPALLETIDLEVEL THEN INVENTDIM.WMSPALLETID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSTATUSIDLEVEL THEN INVENTDIM.INVENTSTATUSID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @LICENSEPLATEIDLEVEL THEN INVENTDIM.LICENSEPLATEID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTBATCHIDLEVEL THEN INVENTDIM.INVENTBATCHID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSERIALIDLEVEL THEN INVENTDIM.INVENTSERIALID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTPROFILEID_RULEVEL THEN INVENTDIM.INVENTPROFILEID_RU ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTOWNERID_RULEVEL THEN INVENTDIM.INVENTOWNERID_RU ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTGTDID_RULEVEL THEN INVENTDIM.INVENTGTDID_RU ELSE NULL END
        #    FROM WHSINVENTRESERVE WHSINVENTRESERVE
        #    JOIN INVENTDIM ON INVENTDIM.INVENTDIMID = WHSINVENTRESERVE.INVENTDIMID AND INVENTDIM.DATAAREAID = WHSINVENTRESERVE.DATAAREAID AND INVENTDIM.PARTITION = WHSINVENTRESERVE.PARTITION
        #    WHERE WHSINVENTRESERVE.ITEMID           = @ITEMID
        #        AND WHSINVENTRESERVE.PARTITION      = @PARTITION
        #        AND WHSINVENTRESERVE.DATAAREAID     = @DATAAREAID
        #        AND WHSINVENTRESERVE.HIERARCHYLEVEL = @LEVEL
        #        AND (@CONFIGID IS NULL OR INVENTDIM.CONFIGID = @CONFIGID)
        #        AND (@INVENTSIZEID IS NULL OR INVENTDIM.INVENTSIZEID = @INVENTSIZEID)
        #        AND (@INVENTCOLORID IS NULL OR INVENTDIM.INVENTCOLORID = @INVENTCOLORID)
        #        AND (@INVENTSTYLEID IS NULL OR INVENTDIM.INVENTSTYLEID = @INVENTSTYLEID)
        #        AND (@LEVEL < @INVENTSITEIDLEVEL OR @INVENTSITEID IS NULL OR @INVENTSITEID = INVENTDIM.INVENTSITEID)
        #        AND (@LEVEL < @INVENTLOCATIONIDLEVEL OR @INVENTLOCATIONID IS NULL OR @INVENTLOCATIONID = INVENTDIM.INVENTLOCATIONID)
        #        AND (@LEVEL < @WMSLOCATIONIDLEVEL OR @WMSLOCATIONID IS NULL OR @WMSLOCATIONID = INVENTDIM.WMSLOCATIONID)
        #        AND (@LEVEL < @WMSPALLETIDLEVEL OR @WMSPALLETID IS NULL OR @WMSPALLETID = INVENTDIM.WMSPALLETID)
        #        AND (@LEVEL < @INVENTSTATUSIDLEVEL OR @INVENTSTATUSID IS NULL OR @INVENTSTATUSID = INVENTDIM.INVENTSTATUSID)
        #        AND (@LEVEL < @LICENSEPLATEIDLEVEL OR @LICENSEPLATEID IS NULL OR @LICENSEPLATEID = INVENTDIM.LICENSEPLATEID)
        #        AND (@LEVEL < @INVENTBATCHIDLEVEL OR @INVENTBATCHID IS NULL OR @INVENTBATCHID = INVENTDIM.INVENTBATCHID)
        #        AND (@LEVEL < @INVENTSERIALIDLEVEL OR @INVENTSERIALID IS NULL OR @INVENTSERIALID = INVENTDIM.INVENTSERIALID)
        #        AND (@LEVEL < @INVENTPROFILEID_RULEVEL OR @INVENTPROFILEID_RU IS NULL OR @INVENTPROFILEID_RU = INVENTDIM.INVENTPROFILEID_RU)
        #        AND (@LEVEL < @INVENTOWNERID_RULEVEL OR @INVENTOWNERID_RU IS NULL OR @INVENTOWNERID_RU = INVENTDIM.INVENTOWNERID_RU)
        #        AND (@LEVEL < @INVENTGTDID_RULEVEL OR @INVENTGTDID_RU IS NULL OR @INVENTGTDID_RU = INVENTDIM.INVENTGTDID_RU)
        #GROUP BY
        #    INVENTDIM.CONFIGID,
        #    INVENTDIM.INVENTSIZEID,
        #    INVENTDIM.INVENTCOLORID,
        #    INVENTDIM.INVENTSTYLEID,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSITEIDLEVEL THEN INVENTDIM.INVENTSITEID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTLOCATIONIDLEVEL THEN INVENTDIM.INVENTLOCATIONID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @WMSLOCATIONIDLEVEL THEN INVENTDIM.WMSLOCATIONID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @WMSPALLETIDLEVEL THEN INVENTDIM.WMSPALLETID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSTATUSIDLEVEL THEN INVENTDIM.INVENTSTATUSID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @LICENSEPLATEIDLEVEL THEN INVENTDIM.LICENSEPLATEID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTBATCHIDLEVEL THEN INVENTDIM.INVENTBATCHID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSERIALIDLEVEL THEN INVENTDIM.INVENTSERIALID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTPROFILEID_RULEVEL THEN INVENTDIM.INVENTPROFILEID_RU ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTOWNERID_RULEVEL THEN INVENTDIM.INVENTOWNERID_RU ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTGTDID_RULEVEL THEN INVENTDIM.INVENTGTDID_RU ELSE NULL END;
        #
        #WHILE (@LEVEL > 0)
        #BEGIN
        #    INSERT INTO @RESULT (AVAILPHYSICALRESULT, AVAILORDEREDRESULT, AVAILPHYSICALFOUND, AVAILORDEREDFOUND, HIERARCHYLEVEL, CONFIGID, INVENTSIZEID, INVENTCOLORID, INVENTSTYLEID, INVENTSITEID, INVENTLOCATIONID, WMSLOCATIONID, WMSPALLETID, INVENTSTATUSID, LICENSEPLATEID, INVENTBATCHID, INVENTSERIALID, INVENTPROFILEID_RU, INVENTOWNERID_RU, INVENTGTDID_RU)
        #SELECT SUM(CASE WHEN RESULT.AVAILPHYSICALFOUND < RESULT.AVAILPHYSICALRESULT THEN RESULT.AVAILPHYSICALFOUND ELSE RESULT.AVAILPHYSICALRESULT END),
        #    SUM(CASE WHEN RESULT.AVAILORDEREDFOUND < RESULT.AVAILORDEREDRESULT THEN RESULT.AVAILORDEREDFOUND ELSE RESULT.AVAILORDEREDRESULT END),
        #    SUM(FOUND.AVAILPHYSICAL),
        #    SUM(FOUND.AVAILORDERED),
        #    @LEVEL + -1,
        #    RESULT.CONFIGID AS CONFIGID,
        #    RESULT.INVENTSIZEID AS INVENTSIZEID,
        #    RESULT.INVENTCOLORID AS INVENTCOLORID,
        #    RESULT.INVENTSTYLEID AS INVENTSTYLEID,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSITEIDLEVEL THEN RESULT.INVENTSITEID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTLOCATIONIDLEVEL THEN RESULT.INVENTLOCATIONID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @WMSLOCATIONIDLEVEL THEN RESULT.WMSLOCATIONID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @WMSPALLETIDLEVEL THEN RESULT.WMSPALLETID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSTATUSIDLEVEL THEN RESULT.INVENTSTATUSID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @LICENSEPLATEIDLEVEL THEN RESULT.LICENSEPLATEID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTBATCHIDLEVEL THEN RESULT.INVENTBATCHID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSERIALIDLEVEL THEN RESULT.INVENTSERIALID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTPROFILEID_RULEVEL THEN RESULT.INVENTPROFILEID_RU ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTOWNERID_RULEVEL THEN RESULT.INVENTOWNERID_RU ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTGTDID_RULEVEL THEN RESULT.INVENTGTDID_RU ELSE NULL END
        #FROM @RESULT RESULT
        #LEFT OUTER JOIN (
        #SELECT SUM(WHSINVENTRESERVE.AVAILPHYSICAL) AS AVAILPHYSICAL,
        #    SUM(WHSINVENTRESERVE.AVAILORDERED) AS AVAILORDERED,
        #    INVENTDIM.CONFIGID AS CONFIGID,
        #    INVENTDIM.INVENTSIZEID AS INVENTSIZEID,
        #    INVENTDIM.INVENTCOLORID AS INVENTCOLORID,
        #    INVENTDIM.INVENTSTYLEID AS INVENTSTYLEID,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSITEIDLEVEL THEN INVENTDIM.INVENTSITEID ELSE NULL END AS INVENTSITEID,
        #    CASE WHEN @LEVEL + -1 >= @INVENTLOCATIONIDLEVEL THEN INVENTDIM.INVENTLOCATIONID ELSE NULL END AS INVENTLOCATIONID,
        #    CASE WHEN @LEVEL + -1 >= @WMSLOCATIONIDLEVEL THEN INVENTDIM.WMSLOCATIONID ELSE NULL END AS WMSLOCATIONID,
        #    CASE WHEN @LEVEL + -1 >= @WMSPALLETIDLEVEL THEN INVENTDIM.WMSPALLETID ELSE NULL END AS WMSPALLETID,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSTATUSIDLEVEL THEN INVENTDIM.INVENTSTATUSID ELSE NULL END AS INVENTSTATUSID,
        #    CASE WHEN @LEVEL + -1 >= @LICENSEPLATEIDLEVEL THEN INVENTDIM.LICENSEPLATEID ELSE NULL END AS LICENSEPLATEID,
        #    CASE WHEN @LEVEL + -1 >= @INVENTBATCHIDLEVEL THEN INVENTDIM.INVENTBATCHID ELSE NULL END AS INVENTBATCHID,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSERIALIDLEVEL THEN INVENTDIM.INVENTSERIALID ELSE NULL END AS INVENTSERIALID,
        #    CASE WHEN @LEVEL + -1 >= @INVENTPROFILEID_RULEVEL THEN INVENTDIM.INVENTPROFILEID_RU ELSE NULL END AS INVENTPROFILEID_RU,
        #    CASE WHEN @LEVEL + -1 >= @INVENTOWNERID_RULEVEL THEN INVENTDIM.INVENTOWNERID_RU ELSE NULL END AS INVENTOWNERID_RU,
        #    CASE WHEN @LEVEL + -1 >= @INVENTGTDID_RULEVEL THEN INVENTDIM.INVENTGTDID_RU ELSE NULL END AS INVENTGTDID_RU
        #FROM WHSINVENTRESERVE WHSINVENTRESERVE
        #JOIN INVENTDIM ON INVENTDIM.PARTITION = WHSINVENTRESERVE.PARTITION
        #    AND INVENTDIM.DATAAREAID  = WHSINVENTRESERVE.DATAAREAID
        #    AND INVENTDIM.INVENTDIMID = WHSINVENTRESERVE.INVENTDIMID
        #WHERE WHSINVENTRESERVE.HIERARCHYLEVEL = @LEVEL + -1
        #    AND WHSINVENTRESERVE.ITEMID       = @ITEMID
        #    AND WHSINVENTRESERVE.PARTITION    = @PARTITION
        #    AND WHSINVENTRESERVE.DATAAREAID   = @DATAAREAID
        #GROUP BY
        #    INVENTDIM.CONFIGID,
        #    INVENTDIM.INVENTSIZEID,
        #    INVENTDIM.INVENTCOLORID,
        #    INVENTDIM.INVENTSTYLEID,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSITEIDLEVEL THEN INVENTDIM.INVENTSITEID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTLOCATIONIDLEVEL THEN INVENTDIM.INVENTLOCATIONID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @WMSLOCATIONIDLEVEL THEN INVENTDIM.WMSLOCATIONID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @WMSPALLETIDLEVEL THEN INVENTDIM.WMSPALLETID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSTATUSIDLEVEL THEN INVENTDIM.INVENTSTATUSID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @LICENSEPLATEIDLEVEL THEN INVENTDIM.LICENSEPLATEID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTBATCHIDLEVEL THEN INVENTDIM.INVENTBATCHID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSERIALIDLEVEL THEN INVENTDIM.INVENTSERIALID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTPROFILEID_RULEVEL THEN INVENTDIM.INVENTPROFILEID_RU ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTOWNERID_RULEVEL THEN INVENTDIM.INVENTOWNERID_RU ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTGTDID_RULEVEL THEN INVENTDIM.INVENTGTDID_RU ELSE NULL END
        #    ) AS FOUND ON (RESULT.CONFIGID = FOUND.CONFIGID)
        #        AND (RESULT.INVENTSIZEID = FOUND.INVENTSIZEID)
        #        AND (RESULT.INVENTCOLORID = FOUND.INVENTCOLORID)
        #        AND (RESULT.INVENTSTYLEID = FOUND.INVENTSTYLEID)
        #        AND (@LEVEL + -1 < @INVENTSITEIDLEVEL OR RESULT.INVENTSITEID = FOUND.INVENTSITEID)
        #        AND (@LEVEL + -1 < @INVENTLOCATIONIDLEVEL OR RESULT.INVENTLOCATIONID = FOUND.INVENTLOCATIONID)
        #        AND (@LEVEL + -1 < @WMSLOCATIONIDLEVEL OR RESULT.WMSLOCATIONID = FOUND.WMSLOCATIONID)
        #        AND (@LEVEL + -1 < @WMSPALLETIDLEVEL OR RESULT.WMSPALLETID = FOUND.WMSPALLETID)
        #        AND (@LEVEL + -1 < @INVENTSTATUSIDLEVEL OR RESULT.INVENTSTATUSID = FOUND.INVENTSTATUSID)
        #        AND (@LEVEL + -1 < @LICENSEPLATEIDLEVEL OR RESULT.LICENSEPLATEID = FOUND.LICENSEPLATEID)
        #        AND (@LEVEL + -1 < @INVENTBATCHIDLEVEL OR RESULT.INVENTBATCHID = FOUND.INVENTBATCHID)
        #        AND (@LEVEL + -1 < @INVENTSERIALIDLEVEL OR RESULT.INVENTSERIALID = FOUND.INVENTSERIALID)
        #        AND (@LEVEL + -1 < @INVENTPROFILEID_RULEVEL OR RESULT.INVENTPROFILEID_RU = FOUND.INVENTPROFILEID_RU)
        #        AND (@LEVEL + -1 < @INVENTOWNERID_RULEVEL OR RESULT.INVENTOWNERID_RU = FOUND.INVENTOWNERID_RU)
        #        AND (@LEVEL + -1 < @INVENTGTDID_RULEVEL OR RESULT.INVENTGTDID_RU = FOUND.INVENTGTDID_RU)
        #WHERE RESULT.HIERARCHYLEVEL = @LEVEL
        #GROUP BY
        #    RESULT.CONFIGID,
        #    RESULT.INVENTSIZEID,
        #    RESULT.INVENTCOLORID,
        #    RESULT.INVENTSTYLEID,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSITEIDLEVEL THEN RESULT.INVENTSITEID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTLOCATIONIDLEVEL THEN RESULT.INVENTLOCATIONID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @WMSLOCATIONIDLEVEL THEN RESULT.WMSLOCATIONID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @WMSPALLETIDLEVEL THEN RESULT.WMSPALLETID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSTATUSIDLEVEL THEN RESULT.INVENTSTATUSID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @LICENSEPLATEIDLEVEL THEN RESULT.LICENSEPLATEID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTBATCHIDLEVEL THEN RESULT.INVENTBATCHID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSERIALIDLEVEL THEN RESULT.INVENTSERIALID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTPROFILEID_RULEVEL THEN RESULT.INVENTPROFILEID_RU ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTOWNERID_RULEVEL THEN RESULT.INVENTOWNERID_RU ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTGTDID_RULEVEL THEN RESULT.INVENTGTDID_RU ELSE NULL END;
        #
        #
        #    SET @LEVEL = @LEVEL - 1
        #END; -- WHILE
        #
        #SELECT @AVAILPHYSICAL = SUM(CASE WHEN AVAILPHYSICALFOUND < AVAILPHYSICALRESULT THEN AVAILPHYSICALFOUND ELSE AVAILPHYSICALRESULT END),
        #    @AVAILORDERED = SUM(CASE WHEN AVAILORDEREDFOUND < AVAILORDEREDRESULT THEN AVAILORDEREDFOUND ELSE AVAILORDEREDRESULT END)
        #FROM @RESULT
        #WHERE HIERARCHYLEVEL = 0;
        #        */
        #
        #        ret += strFmt('CREATE PROCEDURE [%1]\n', WhsOnHandSPHelper::storedProcedureName(_includeDelta));
        #        ret +=        '    @PARTITION bigint,\n';
        #        ret += strFmt('    %1 int,\n', this.levelExpression(0));
        #        ret += strFmt('    @DATAAREAID %1,\n', this.sqlTypeNameForField(tableNum(InventTable), fieldNum(InventTable, DataAreaId)));
        #        ret += strFmt('    @ITEMID %1,\n', this.sqlTypeNameForField(tableNum(InventTable), fieldNum(InventTable, ItemId)));
        #        if (_includeDelta)
        #        {
        #            ret += strFmt('    %1 %2,\n', #TTSIdParmName, this.sqlTypeNameForField(tableNum(WHSInventReserveDelta), fieldNum(WHSInventReserveDelta, TTSId)));
        #        }
        #
        #        // Generate parameters for supported dimensions
        #        enabledDimsEnumerator.reset();
        #        while (enabledDimsEnumerator.moveNext())
        #        {
        #            [dimFieldId, dimFieldName] = enabledDimsEnumerator.current();
        #            ret += strFmt('    %1 %2 = NULL,\n', this.dimensionParm(dimFieldName), this.sqlTypeNameForField(tableNum(InventDim), dimFieldId));
        #            ret += strFmt('    %1 int = %2,\n', this.dimensionLevelParm(dimFieldName), #INVALIDHIERARCHYLEVEL);
        #        }
        #
        #        // Generate parameters for product dimensions
        #        productDimensionsEnumerator.reset();
        #        while (productDimensionsEnumerator.moveNext())
        #        {
        #            [dimFieldId, dimFieldName] = productDimensionsEnumerator.current();
        #            ret += strFmt('    %1 %2 = NULL,\n', this.dimensionParm(dimFieldName), this.sqlTypeNameForField(tableNum(InventDim), dimFieldId));
        #        }
        #
        #        ret +=        '    -- Output parameters\n';
        #        ret += strFmt('    %1  numeric(32, 16) OUTPUT,\n', #AvailPhysicalParmName);
        #        ret += strFmt('    %1  numeric(32, 16) OUTPUT\n', #AvailOrderedParmName);
        #        ret +=        'AS\n\n';
        #
        #        ret +=        '-- Without nocount on the procedure will return number of affected rows in the result set which is\n';
        #        ret +=        '-- harmful to performance and requires us to adjust field retrieval when calling the stored procedure\n';
        #        ret +=        'SET NOCOUNT ON;\n\n';
        #
        #        ret +=        '-- We need to supress warnings when trying to aggregate NULL columns. This is a valid business scenario\n';
        #        ret +=        '-- and happens e.g. for a non-LP controlled location with batch below item.\n';
        #        ret +=        'SET ANSI_WARNINGS OFF;\n\n';
        #
        #        ret += this.declareResultTableVariable() + '\n';
        #
        #        ret +=        '-- The algorithm of the stored procedure is as follows:\n';
        #        ret +=        '-- 1. Initialize the @RESULT table with aggregated qtys in WHSInventReserve for the level requested by user\n';
        #        ret +=        '-- 2. Traverse the hierarchy up and for each level:\n';
        #        ret +=        '--     1) Aggregate qtys from previous level in @RESULT grouping by dimensions of current hierarchy level\n';
        #        ret +=        '--     2) Aggregate qtys from current level in WHSInventReserve grouping by dimensions of current hierarchy level\n';
        #        ret +=        '--     3) Select the minimum of the above qtys and insert into @RESULT\n';
        #        ret +=        '-- 3. When hierarchy level 0 is reached, return the sum of aggregated qtys in @RESULT\n\n';
        #
        #        ret +=        '-- Initialize the @RESULT variable.\n';
        #        ret +=        '-- Loads aggregated data for the lowest level requested by user.\n';
        #        ret +=        'INSERT INTO @RESULT (' + this.resultTableVariableFields() + ')\n';
        #
        #        ret += this.selectFromWHSInventReserveStmt(_includeDelta) + '\n';
        #
        #        ret += strFmt('WHILE (%1 > 0)\n', this.levelExpression(0));
        #        ret +=        'BEGIN\n';
        #        ret += strFmt('    INSERT INTO @RESULT (%1)\n', this.resultTableVariableFields());
        #
        #        ret += this.selectFromResultJoinWHSInventReserveStmt(_includeDelta) + '\n\n';
        #
        #        ret += strFmt('    SET %1 = %1 - 1\n', this.levelExpression(0));
        #        ret +=        'END; -- WHILE\n\n';
        #
        #        ret += strFmt('SELECT %1 = SUM(CASE WHEN %2 < %3 THEN %2 ELSE %3 END),\n',
        #                        #AvailPhysicalParmName,
        #                        #AVAILPHYSICALFOUNDFieldName,
        #                        #AVAILPHYSICALRESULTFieldName);
        #        ret += strFmt('    %1 = SUM(CASE WHEN %2 < %3 THEN %2 ELSE %3 END)\n',
        #                        #AvailOrderedParmName,
        #                        #AVAILORDEREDFOUNDFieldName,
        #                        #AVAILORDEREDRESULTFieldName);
        #        ret +=        'FROM @RESULT\n';
        #        ret += strFmt('WHERE %1 = 0;\n', #HIERARCHYLEVELFieldName);
        #    }
        #
        #    return ret;
        #}
      ENDSOURCE
      SOURCE #classDeclaration
        #///
        #///     The WHSOnHandSPHelper class contains helper methods to manipulate the WHS on-hand stored procedure.
        #///
        #public class WhsOnHandSPHelper
        #{
        #    #Define.CacheScope('WHSOnHandSPHelper')
        #    #Define.EnabledDimsParametersCacheKey('EnabledDimensionsCache')
        #    #Define.ProductDimsParametersCacheKey('ProductDimensionsCache')
        #
        #    #Define.AvailPhysicalParmName('@AVAILPHYSICAL')
        #    #Define.AvailPhysicalMinName('@AVAILPHYSICALMIN')
        #    #Define.AvailOrderedParmName('@AVAILORDERED')
        #    #Define.AvailOrderedMinName('@AVAILORDERMIN')
        #    #Define.TTSIdParmName('@TTSId')
        #
        #    #Define.AVAILPHYSICALRESULTFieldName('AVAILPHYSICALRESULT')
        #    #Define.AVAILORDEREDRESULTFieldName('AVAILORDEREDRESULT')
        #    #Define.AVAILPHYSICALFOUNDFieldName('AVAILPHYSICALFOUND')
        #    #Define.AVAILORDEREDFOUNDFieldName('AVAILORDEREDFOUND')
        #    #Define.HIERARCHYLEVELFieldName('HIERARCHYLEVEL')
        #
        #    #WHSOnHandSP
        #    #WHSReservationHierarchy
        #
        #    str resultTableVariableFields; // access using the resultTableVariableFields() method
        #
        #    boolean     useOptimizedVersion;
        #}
      ENDSOURCE
      SOURCE #new
        #protected void new()
        #{
        #    resultTableVariableFields = '';
        #
        #    useOptimizedVersion = true;
        #}
      ENDSOURCE
      SOURCE #selectFromWHSInventReserveStmt
        #private str selectFromWHSInventReserveStmt(boolean _includeDelta)
        #{
        #    str             ret = '';
        #    ListEnumerator  enabledDimsEnumerator = this.enabledDimensions().getEnumerator();
        #    ListEnumerator  productDimsEnumerator = this.productDimensions().getEnumerator();
        #    str             dimFieldName;
        #    str             levelExpr = this.levelExpression(-1);
        #    str             inventReserveAlias = this.whsInventReserveTableAlias(_includeDelta);
        #    boolean         hasDoneFirstIteration;
        #
        #    if(useOptimizedVersion)
        #    {
        #        ret = this.TEC_selectFromWHSInventReserveStmt(_includeDelta);
        #    }
        #    else
        #    {
        #        ret += strFmt('SELECT SUM(%1.AVAILPHYSICAL),\n', inventReserveAlias);
        #        ret += strFmt('    SUM(%1.AVAILORDERED),\n', inventReserveAlias);
        #        ret +=        '    NULL,\n';
        #        ret +=        '    NULL,\n';
        #        ret += strFmt('    %1,\n', this.levelExpression(0));
        #
        #        // Select product dimensions
        #        productDimsEnumerator.reset();
        #        while (productDimsEnumerator.moveNext())
        #        {
        #            dimFieldName = conPeek(productDimsEnumerator.current(), 2);
        #            ret += strFmt('    INVENTDIM.%1,\n', dimFieldName);
        #        }
        #
        #        // Select enabled dimensions
        #        enabledDimsEnumerator.reset();
        #        hasDoneFirstIteration = false;
        #        while (enabledDimsEnumerator.moveNext())
        #        {
        #            if (hasDoneFirstIteration)
        #            {
        #                ret += ',\n';
        #            }
        #
        #            dimFieldName = conPeek(enabledDimsEnumerator.current(), 2);
        #            ret += strFmt('    CASE WHEN %1 >= %2 THEN INVENTDIM.%3 ELSE NULL END',
        #                            this.levelExpression(-1),
        #                            this.dimensionLevelParm(dimFieldName),
        #                            dimFieldName);
        #
        #            hasDoneFirstIteration = true;
        #        }
        #        ret += '\n';
        #
        #        ret += strFmt('    FROM %1 %2\n', this.whsInventReserveTableName(_includeDelta), inventReserveAlias);
        #        ret += strFmt('    JOIN INVENTDIM ON INVENTDIM.INVENTDIMID = %1.INVENTDIMID AND INVENTDIM.DATAAREAID = %1.DATAAREAID AND INVENTDIM.PARTITION = %1.PARTITION\n', inventReserveAlias);
        #        ret += strFmt('    WHERE %1.ITEMID           = @ITEMID\n', inventReserveAlias);
        #        ret += strFmt('        AND %1.PARTITION      = @PARTITION\n', inventReserveAlias);
        #        ret += strFmt('        AND %1.DATAAREAID     = @DATAAREAID\n', inventReserveAlias);
        #        ret += strFmt('        AND %1.HIERARCHYLEVEL = %2\n', inventReserveAlias, this.levelExpression(0));
        #
        #        // Add product dimensions range
        #        productDimsEnumerator.reset();
        #        while (productDimsEnumerator.moveNext())
        #        {
        #            dimFieldName = conPeek(productDimsEnumerator.current(), 2);
        #            ret += strFmt('        AND (%2 IS NULL OR INVENTDIM.%1 = %2)\n',
        #                            dimFieldName,
        #                            this.dimensionParm(dimFieldName));
        #        }
        #
        #        // Add enabled dimensions range
        #        enabledDimsEnumerator.reset();
        #        while (enabledDimsEnumerator.moveNext())
        #        {
        #            dimFieldName = conPeek(enabledDimsEnumerator.current(), 2);
        #            ret += strFmt('        AND (%1 < %2 OR %3 IS NULL OR %3 = INVENTDIM.%4)\n',
        #                            this.levelExpression(0),
        #                            this.dimensionLevelParm(dimFieldName),
        #                            this.dimensionParm(dimFieldName),
        #                            dimFieldName);
        #        }
        #
        #        ret +=        'GROUP BY\n';
        #
        #        // Add grouping by product dimensions
        #        productDimsEnumerator.reset();
        #        while (productDimsEnumerator.moveNext())
        #        {
        #            dimFieldName = conPeek(productDimsEnumerator.current(), 2);
        #            ret += strFmt('    INVENTDIM.%1,\n', dimFieldName);
        #        }
        #
        #        // Add grouping by enabled dimensions
        #        enabledDimsEnumerator.reset();
        #        hasDoneFirstIteration = false;
        #        while (enabledDimsEnumerator.moveNext())
        #        {
        #            if (hasDoneFirstIteration)
        #            {
        #                ret += ',\n';
        #            }
        #
        #            dimFieldName = conPeek(enabledDimsEnumerator.current(), 2);
        #            ret += strFmt('    CASE WHEN %1 >= %2 THEN INVENTDIM.%3 ELSE NULL END',
        #                            this.levelExpression(-1),
        #                            this.dimensionLevelParm(dimFieldName),
        #                            dimFieldName);
        #
        #            hasDoneFirstIteration = true;
        #        }
        #
        #        ret += ';\n';
        #    }
        #
        #    return ret;
        #}
      ENDSOURCE
      SOURCE #selectInnerFromWHSInventReserveStmt
        #private str selectInnerFromWHSInventReserveStmt(boolean _includeDelta)
        #{
        #    str             ret = '';
        #    ListEnumerator  enabledDimsEnumerator = this.enabledDimensions().getEnumerator();
        #    ListEnumerator  productDimsEnumerator = this.productDimensions().getEnumerator();
        #    str             dimFieldName;
        #    str             levelExpr = this.levelExpression(-1);
        #    str             inventReserveAlias = this.whsInventReserveTableAlias(_includeDelta);
        #    boolean         hasDoneFirstIteration;
        #
        #    if(useOptimizedVersion)
        #    {
        #        ret = this.TEC_selectInnerFromWHSInventReserveStmt(_includeDelta);
        #    }
        #    else
        #    {
        #        ret += strFmt('SELECT SUM(%1.AVAILPHYSICAL) AS AVAILPHYSICAL,\n', inventReserveAlias);
        #        ret += strFmt('    SUM(%1.AVAILORDERED) AS AVAILORDERED,\n', inventReserveAlias);
        #
        #        productDimsEnumerator.reset();
        #        while (productDimsEnumerator.moveNext())
        #        {
        #            dimFieldName = conPeek(productDimsEnumerator.current(), 2);
        #            ret += strFmt('    INVENTDIM.%1 AS %1,\n', dimFieldName);
        #        }
        #
        #        enabledDimsEnumerator.reset();
        #        hasDoneFirstIteration = false;
        #        while (enabledDimsEnumerator.moveNext())
        #        {
        #            if (hasDoneFirstIteration)
        #            {
        #                ret += ',\n';
        #            }
        #
        #            dimFieldName = conPeek(enabledDimsEnumerator.current(), 2);
        #            ret += strFmt('    CASE WHEN %1 >= %2 THEN INVENTDIM.%3 ELSE NULL END AS %3',
        #                            levelExpr,
        #                            this.dimensionLevelParm(dimFieldName),
        #                            dimFieldName);
        #
        #            hasDoneFirstIteration = true;
        #        }
        #        ret += '\n';
        #
        #        ret += strFmt('FROM %1 %2\n', this.whsInventReserveTableName(_includeDelta), inventReserveAlias);
        #        ret += strFmt('JOIN INVENTDIM ON INVENTDIM.PARTITION = %1.PARTITION\n', inventReserveAlias);
        #        ret += strFmt('    AND INVENTDIM.DATAAREAID  = %1.DATAAREAID\n', inventReserveAlias);
        #        ret += strFmt('    AND INVENTDIM.INVENTDIMID = %1.INVENTDIMID\n', inventReserveAlias);
        #        ret += strFmt('WHERE %1.HIERARCHYLEVEL = %2\n', inventReserveAlias, levelExpr);
        #        ret += strFmt('    AND %1.ITEMID       = @ITEMID\n', inventReserveAlias);
        #        ret += strFmt('    AND %1.PARTITION    = @PARTITION\n', inventReserveAlias);
        #        ret += strFmt('    AND %1.DATAAREAID   = @DATAAREAID\n', inventReserveAlias);
        #        ret +=          'GROUP BY\n';
        #
        #        // Add groupping by product dimensions
        #        productDimsEnumerator.reset();
        #        while (productDimsEnumerator.moveNext())
        #        {
        #            dimFieldName = conPeek(productDimsEnumerator.current(), 2);
        #            ret += strFmt('    INVENTDIM.%1,\n', dimFieldName);
        #        }
        #
        #        // Add groupping by enabled dimensions
        #        enabledDimsEnumerator.reset();
        #        hasDoneFirstIteration = false;
        #        while (enabledDimsEnumerator.moveNext())
        #        {
        #            if (hasDoneFirstIteration)
        #            {
        #                ret += ',\n';
        #            }
        #
        #            dimFieldName = conPeek(enabledDimsEnumerator.current(), 2);
        #            ret += strFmt('    CASE WHEN %1 >= %2 THEN INVENTDIM.%3 ELSE NULL END',
        #                            levelExpr,
        #                            this.dimensionLevelParm(dimFieldName),
        #                            dimFieldName);
        #
        #            hasDoneFirstIteration = true;
        #        }
        #        ret += '\n';
        #    }
        #
        #    return ret;
        #}
      ENDSOURCE
      SOURCE #TEC_buildCreateSPStmt
        #private str TEC_buildCreateSPStmt(boolean _includeDelta)
        #{
        #    str             ret = '';
        #    ListEnumerator  enabledDimsEnumerator;
        #    ListEnumerator  productDimensionsEnumerator;
        #    FieldId         dimFieldId;
        #    str             dimFieldName;
        #    boolean         isSerialEnabled;
        #    str             inventReserveAlias = this.whsInventReserveTableAlias(_includeDelta);
        #
        #    InventDimMetaDataCache::clearCache(); //in some cases the cache is not updated before sync making below SP
        #
        #    enabledDimsEnumerator = this.enabledDimensions().getEnumerator();
        #    productDimensionsEnumerator = this.productDimensions().getEnumerator();
        #    isSerialEnabled = this.getEnabledDimsParametersMap().exists(fieldNum(InventDim, InventSerialId));
        #
        #    /*
        #CREATE PROCEDURE [dbo].[sp_WHSOnHand]
        #    @PARTITION bigint,
        #    @LEVEL int,
        #    @DATAAREAID nvarchar (4),
        #    @ITEMID nvarchar (20),
        #    @INVENTSITEID nvarchar (10) = NULL,
        #    @INVENTSITEIDLEVEL int = 1024,
        #    @INVENTLOCATIONID nvarchar (10) = NULL,
        #    @INVENTLOCATIONIDLEVEL int = 1024,
        #    @WMSLOCATIONID nvarchar (10) = NULL,
        #    @WMSLOCATIONIDLEVEL int = 1024,
        #    @WMSPALLETID nvarchar (18) = NULL,
        #    @WMSPALLETIDLEVEL int = 1024,
        #    @INVENTSTATUSID nvarchar (10) = NULL,
        #    @INVENTSTATUSIDLEVEL int = 1024,
        #    @LICENSEPLATEID nvarchar (25) = NULL,
        #    @LICENSEPLATEIDLEVEL int = 1024,
        #    @INVENTBATCHID nvarchar (20) = NULL,
        #    @INVENTBATCHIDLEVEL int = 1024,
        #    @INVENTSERIALID nvarchar (20) = NULL,
        #    @INVENTSERIALIDLEVEL int = 1024,
        #    @INVENTPROFILEID_RU nvarchar (10) = NULL,
        #    @INVENTPROFILEID_RULEVEL int = 1024,
        #    @INVENTOWNERID_RU nvarchar (20) = NULL,
        #    @INVENTOWNERID_RULEVEL int = 1024,
        #    @INVENTGTDID_RU nvarchar (30) = NULL,
        #    @INVENTGTDID_RULEVEL int = 1024,
        #    @CONFIGID nvarchar (10) = NULL,
        #    @INVENTSIZEID nvarchar (10) = NULL,
        #    @INVENTCOLORID nvarchar (10) = NULL,
        #    @INVENTSTYLEID nvarchar (10) = NULL,
        #    -- Output parameters
        #    @AVAILPHYSICAL  numeric(32, 16) OUTPUT,
        #    @AVAILORDERED  numeric(32, 16) OUTPUT
        #AS
        #
        #-- Without nocount on the procedure will return number of affected rows in the result set which is
        #-- harmful to performance and requires us to adjust field retrieval when calling the stored procedure
        #SET NOCOUNT ON;
        #
        #-- We need to supress warnings when trying to aggregate NULL columns. This is a valid business scenario
        #-- and happens e.g. for a non-LP controlled location with batch below item.
        #SET ANSI_WARNINGS OFF;
        #
        #-- The @RESULT table variable is used to store intermediate quantities.
        #DECLARE @RESULT TABLE(
        #    AVAILPHYSICALRESULT numeric(32, 16) NOT NULL, -- Aggregated minimum qtys from lower level in @RESULT
        #    AVAILORDEREDRESULT numeric(32, 16) NOT NULL, -- Aggregated minimum qtys from lower level in @RESULT
        #    AVAILPHYSICALFOUND numeric(32, 16), -- Aggregated qty for current level in WHSInventReserve
        #    AVAILORDEREDFOUND numeric(32, 16), -- Aggregated qty for current level in WHSInventReserve
        #    HIERARCHYLEVEL int NOT NULL,
        #    CONFIGID nvarchar (10) NULL,
        #    INVENTSIZEID nvarchar (10) NULL,
        #    INVENTCOLORID nvarchar (10) NULL,
        #    INVENTSTYLEID nvarchar (10) NULL,
        #    INVENTSITEID nvarchar (10) NULL,
        #    INVENTLOCATIONID nvarchar (10) NULL,
        #    WMSLOCATIONID nvarchar (10) NULL,
        #    WMSPALLETID nvarchar (18) NULL,
        #    INVENTSTATUSID nvarchar (10) NULL,
        #    LICENSEPLATEID nvarchar (25) NULL,
        #    INVENTBATCHID nvarchar (20) NULL,
        #    INVENTSERIALID nvarchar (20) NULL,
        #    INVENTPROFILEID_RU nvarchar (10) NULL,
        #    INVENTOWNERID_RU nvarchar (20) NULL,
        #    INVENTGTDID_RU nvarchar (30) NULL);
        #
        #DECLARE @DIMLIST TABLE(
        #    INVENTDIMID nvarchar (20) NULL,
        #    AVAILPHYSICAL numeric(32, 16) NOT NULL,
        #    AVAILORDERED numeric(32, 16) NOT NULL);
        #
        #-- The algorithm of the stored procedure is as follows:
        #-- 1. Initialize the @RESULT table with aggregated qtys in WHSInventReserve for the level requested by user
        #-- 2. Traverse the hierarchy up and for each level:
        #--     1) Aggregate qtys from previous level in @RESULT grouping by dimensions of current hierarchy level
        #--     2) Aggregate qtys from current level in WHSInventReserve grouping by dimensions of current hierarchy level
        #--     3) Select the minimum of the above qtys and insert into @RESULT
        #-- 3. When hierarchy level 0 is reached, return the sum of aggregated qtys in @RESULT
        #
        #-- In the original query (the one after below query), it is joining InventDim and WHSInventReserve and filter records based on some criteria.
        #-- But in the execution plan, it doesn't seems to generate a good plan (even after UPDATE STATISTICS). In the original execution plan, it always
        #-- Get records from InventDim first, which returns lot of rows, then only join it to WHSInventReserve and continue filter from there.
        #-- If we makes it get records from WHSInventReserve first, filter it, then with lesser rows from here, join it to InventDim, it will be quicker.
        #-- So the optimization is to filter WHSInventResreve first, then use this result at the original query. In the test, this has reduce the execution
        #-- time from ~250ms to ~7ms
        #  INSERT INTO @DIMLIST(INVENTDIMID, AVAILPHYSICAL, AVAILORDERED)
        #  SELECT INVENTDIMID, AVAILPHYSICAL, AVAILORDERED
        #    FROM WHSINVENTRESERVE WHSINVENTRESERVE
        #   WHERE WHSINVENTRESERVE.ITEMID         = @ITEMID
        #     AND WHSINVENTRESERVE.PARTITION      = @PARTITION
        #     AND WHSINVENTRESERVE.DATAAREAID     = @DATAAREAID
        #     AND WHSINVENTRESERVE.HIERARCHYLEVEL = @LEVEL
        #
        #-- Initialize the @RESULT variable.
        #-- Loads aggregated data for the lowest level requested by user.
        #INSERT INTO @RESULT (AVAILPHYSICALRESULT, AVAILORDEREDRESULT, AVAILPHYSICALFOUND, AVAILORDEREDFOUND, HIERARCHYLEVEL, CONFIGID, INVENTSIZEID, INVENTCOLORID, INVENTSTYLEID, INVENTSITEID, INVENTLOCATIONID, WMSLOCATIONID, WMSPALLETID, INVENTSTATUSID, LICENSEPLATEID, INVENTBATCHID, INVENTSERIALID, INVENTPROFILEID_RU, INVENTOWNERID_RU, INVENTGTDID_RU)
        #SELECT SUM(DIMLIST.AVAILPHYSICAL),
        #    SUM(DIMLIST.AVAILORDERED),
        #    NULL,
        #    NULL,
        #    @LEVEL,
        #    INVENTDIM.CONFIGID,
        #    INVENTDIM.INVENTSIZEID,
        #    INVENTDIM.INVENTCOLORID,
        #    INVENTDIM.INVENTSTYLEID,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSITEIDLEVEL THEN INVENTDIM.INVENTSITEID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTLOCATIONIDLEVEL THEN INVENTDIM.INVENTLOCATIONID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @WMSLOCATIONIDLEVEL THEN INVENTDIM.WMSLOCATIONID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @WMSPALLETIDLEVEL THEN INVENTDIM.WMSPALLETID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSTATUSIDLEVEL THEN INVENTDIM.INVENTSTATUSID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @LICENSEPLATEIDLEVEL THEN INVENTDIM.LICENSEPLATEID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTBATCHIDLEVEL THEN INVENTDIM.INVENTBATCHID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSERIALIDLEVEL THEN INVENTDIM.INVENTSERIALID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTPROFILEID_RULEVEL THEN INVENTDIM.INVENTPROFILEID_RU ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTOWNERID_RULEVEL THEN INVENTDIM.INVENTOWNERID_RU ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTGTDID_RULEVEL THEN INVENTDIM.INVENTGTDID_RU ELSE NULL END
        #    FROM INVENTDIM INVENTDIM
        #    JOIN @DIMLIST DIMLIST
        #      ON INVENTDIM.INVENTDIMID = DIMLIST.INVENTDIMID
        #    WHERE   INVENTDIM.DATAAREAID = @DATAAREAID
        #        AND INVENTDIM.PARTITION = @PARTITION
        #        AND (@CONFIGID IS NULL OR INVENTDIM.CONFIGID = @CONFIGID)
        #        AND (@INVENTSIZEID IS NULL OR INVENTDIM.INVENTSIZEID = @INVENTSIZEID)
        #        AND (@INVENTCOLORID IS NULL OR INVENTDIM.INVENTCOLORID = @INVENTCOLORID)
        #        AND (@INVENTSTYLEID IS NULL OR INVENTDIM.INVENTSTYLEID = @INVENTSTYLEID)
        #        AND (@LEVEL < @INVENTSITEIDLEVEL OR @INVENTSITEID IS NULL OR @INVENTSITEID = INVENTDIM.INVENTSITEID)
        #        AND (@LEVEL < @INVENTLOCATIONIDLEVEL OR @INVENTLOCATIONID IS NULL OR @INVENTLOCATIONID = INVENTDIM.INVENTLOCATIONID)
        #        AND (@LEVEL < @WMSLOCATIONIDLEVEL OR @WMSLOCATIONID IS NULL OR @WMSLOCATIONID = INVENTDIM.WMSLOCATIONID)
        #        AND (@LEVEL < @WMSPALLETIDLEVEL OR @WMSPALLETID IS NULL OR @WMSPALLETID = INVENTDIM.WMSPALLETID)
        #        AND (@LEVEL < @INVENTSTATUSIDLEVEL OR @INVENTSTATUSID IS NULL OR @INVENTSTATUSID = INVENTDIM.INVENTSTATUSID)
        #        AND (@LEVEL < @LICENSEPLATEIDLEVEL OR @LICENSEPLATEID IS NULL OR @LICENSEPLATEID = INVENTDIM.LICENSEPLATEID)
        #        AND (@LEVEL < @INVENTBATCHIDLEVEL OR @INVENTBATCHID IS NULL OR @INVENTBATCHID = INVENTDIM.INVENTBATCHID)
        #        AND (@LEVEL < @INVENTSERIALIDLEVEL OR @INVENTSERIALID IS NULL OR @INVENTSERIALID = INVENTDIM.INVENTSERIALID)
        #        AND (@LEVEL < @INVENTPROFILEID_RULEVEL OR @INVENTPROFILEID_RU IS NULL OR @INVENTPROFILEID_RU = INVENTDIM.INVENTPROFILEID_RU)
        #        AND (@LEVEL < @INVENTOWNERID_RULEVEL OR @INVENTOWNERID_RU IS NULL OR @INVENTOWNERID_RU = INVENTDIM.INVENTOWNERID_RU)
        #        AND (@LEVEL < @INVENTGTDID_RULEVEL OR @INVENTGTDID_RU IS NULL OR @INVENTGTDID_RU = INVENTDIM.INVENTGTDID_RU)
        #GROUP BY
        #    INVENTDIM.CONFIGID,
        #    INVENTDIM.INVENTSIZEID,
        #    INVENTDIM.INVENTCOLORID,
        #    INVENTDIM.INVENTSTYLEID,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSITEIDLEVEL THEN INVENTDIM.INVENTSITEID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTLOCATIONIDLEVEL THEN INVENTDIM.INVENTLOCATIONID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @WMSLOCATIONIDLEVEL THEN INVENTDIM.WMSLOCATIONID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @WMSPALLETIDLEVEL THEN INVENTDIM.WMSPALLETID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSTATUSIDLEVEL THEN INVENTDIM.INVENTSTATUSID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @LICENSEPLATEIDLEVEL THEN INVENTDIM.LICENSEPLATEID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTBATCHIDLEVEL THEN INVENTDIM.INVENTBATCHID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSERIALIDLEVEL THEN INVENTDIM.INVENTSERIALID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTPROFILEID_RULEVEL THEN INVENTDIM.INVENTPROFILEID_RU ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTOWNERID_RULEVEL THEN INVENTDIM.INVENTOWNERID_RU ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTGTDID_RULEVEL THEN INVENTDIM.INVENTGTDID_RU ELSE NULL END;
        #
        #WHILE (@LEVEL > 0)
        #BEGIN
        #
        #  DELETE FROM @DIMLIST
        #
        #  INSERT INTO @DIMLIST(INVENTDIMID, AVAILPHYSICAL, AVAILORDERED)
        #  SELECT INVENTDIMID, AVAILPHYSICAL, AVAILORDERED
        #    FROM WHSINVENTRESERVE WHSINVENTRESERVE
        #   WHERE WHSINVENTRESERVE.HIERARCHYLEVEL = @LEVEL + -1
        #     AND WHSINVENTRESERVE.ITEMID         = @ITEMID
        #     AND WHSINVENTRESERVE.PARTITION      = @PARTITION
        #     AND WHSINVENTRESERVE.DATAAREAID     = @DATAAREAID
        #
        #INSERT INTO @RESULT (AVAILPHYSICALRESULT, AVAILORDEREDRESULT, AVAILPHYSICALFOUND, AVAILORDEREDFOUND, HIERARCHYLEVEL, CONFIGID, INVENTSIZEID, INVENTCOLORID, INVENTSTYLEID, INVENTSITEID, INVENTLOCATIONID, WMSLOCATIONID, WMSPALLETID, INVENTSTATUSID, LICENSEPLATEID, INVENTBATCHID, INVENTSERIALID, INVENTPROFILEID_RU, INVENTOWNERID_RU, INVENTGTDID_RU)
        #SELECT SUM(CASE WHEN RESULT.AVAILPHYSICALFOUND < RESULT.AVAILPHYSICALRESULT THEN RESULT.AVAILPHYSICALFOUND ELSE RESULT.AVAILPHYSICALRESULT END),
        #    SUM(CASE WHEN RESULT.AVAILORDEREDFOUND < RESULT.AVAILORDEREDRESULT THEN RESULT.AVAILORDEREDFOUND ELSE RESULT.AVAILORDEREDRESULT END),
        #    SUM(FOUND.AVAILPHYSICAL),
        #    SUM(FOUND.AVAILORDERED),
        #    @LEVEL + -1,
        #    RESULT.CONFIGID AS CONFIGID,
        #    RESULT.INVENTSIZEID AS INVENTSIZEID,
        #    RESULT.INVENTCOLORID AS INVENTCOLORID,
        #    RESULT.INVENTSTYLEID AS INVENTSTYLEID,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSITEIDLEVEL THEN RESULT.INVENTSITEID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTLOCATIONIDLEVEL THEN RESULT.INVENTLOCATIONID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @WMSLOCATIONIDLEVEL THEN RESULT.WMSLOCATIONID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @WMSPALLETIDLEVEL THEN RESULT.WMSPALLETID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSTATUSIDLEVEL THEN RESULT.INVENTSTATUSID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @LICENSEPLATEIDLEVEL THEN RESULT.LICENSEPLATEID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTBATCHIDLEVEL THEN RESULT.INVENTBATCHID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSERIALIDLEVEL THEN RESULT.INVENTSERIALID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTPROFILEID_RULEVEL THEN RESULT.INVENTPROFILEID_RU ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTOWNERID_RULEVEL THEN RESULT.INVENTOWNERID_RU ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTGTDID_RULEVEL THEN RESULT.INVENTGTDID_RU ELSE NULL END
        #FROM @RESULT RESULT
        #LEFT OUTER JOIN (
        #SELECT SUM(DIMLIST.AVAILPHYSICAL) AS AVAILPHYSICAL,
        #    SUM(DIMLIST.AVAILORDERED) AS AVAILORDERED,
        #    INVENTDIM.CONFIGID AS CONFIGID,
        #    INVENTDIM.INVENTSIZEID AS INVENTSIZEID,
        #    INVENTDIM.INVENTCOLORID AS INVENTCOLORID,
        #    INVENTDIM.INVENTSTYLEID AS INVENTSTYLEID,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSITEIDLEVEL THEN INVENTDIM.INVENTSITEID ELSE NULL END AS INVENTSITEID,
        #    CASE WHEN @LEVEL + -1 >= @INVENTLOCATIONIDLEVEL THEN INVENTDIM.INVENTLOCATIONID ELSE NULL END AS INVENTLOCATIONID,
        #    CASE WHEN @LEVEL + -1 >= @WMSLOCATIONIDLEVEL THEN INVENTDIM.WMSLOCATIONID ELSE NULL END AS WMSLOCATIONID,
        #    CASE WHEN @LEVEL + -1 >= @WMSPALLETIDLEVEL THEN INVENTDIM.WMSPALLETID ELSE NULL END AS WMSPALLETID,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSTATUSIDLEVEL THEN INVENTDIM.INVENTSTATUSID ELSE NULL END AS INVENTSTATUSID,
        #    CASE WHEN @LEVEL + -1 >= @LICENSEPLATEIDLEVEL THEN INVENTDIM.LICENSEPLATEID ELSE NULL END AS LICENSEPLATEID,
        #    CASE WHEN @LEVEL + -1 >= @INVENTBATCHIDLEVEL THEN INVENTDIM.INVENTBATCHID ELSE NULL END AS INVENTBATCHID,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSERIALIDLEVEL THEN INVENTDIM.INVENTSERIALID ELSE NULL END AS INVENTSERIALID,
        #    CASE WHEN @LEVEL + -1 >= @INVENTPROFILEID_RULEVEL THEN INVENTDIM.INVENTPROFILEID_RU ELSE NULL END AS INVENTPROFILEID_RU,
        #    CASE WHEN @LEVEL + -1 >= @INVENTOWNERID_RULEVEL THEN INVENTDIM.INVENTOWNERID_RU ELSE NULL END AS INVENTOWNERID_RU,
        #    CASE WHEN @LEVEL + -1 >= @INVENTGTDID_RULEVEL THEN INVENTDIM.INVENTGTDID_RU ELSE NULL END AS INVENTGTDID_RU
        #FROM INVENTDIM INVENTDIM
        #JOIN @DIMLIST DIMLIST
        #     ON INVENTDIM.PARTITION = @PARTITION
        #    AND INVENTDIM.DATAAREAID  = @DATAAREAID
        #    AND INVENTDIM.INVENTDIMID = DIMLIST.INVENTDIMID
        #GROUP BY
        #    INVENTDIM.CONFIGID,
        #    INVENTDIM.INVENTSIZEID,
        #    INVENTDIM.INVENTCOLORID,
        #    INVENTDIM.INVENTSTYLEID,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSITEIDLEVEL THEN INVENTDIM.INVENTSITEID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTLOCATIONIDLEVEL THEN INVENTDIM.INVENTLOCATIONID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @WMSLOCATIONIDLEVEL THEN INVENTDIM.WMSLOCATIONID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @WMSPALLETIDLEVEL THEN INVENTDIM.WMSPALLETID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSTATUSIDLEVEL THEN INVENTDIM.INVENTSTATUSID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @LICENSEPLATEIDLEVEL THEN INVENTDIM.LICENSEPLATEID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTBATCHIDLEVEL THEN INVENTDIM.INVENTBATCHID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSERIALIDLEVEL THEN INVENTDIM.INVENTSERIALID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTPROFILEID_RULEVEL THEN INVENTDIM.INVENTPROFILEID_RU ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTOWNERID_RULEVEL THEN INVENTDIM.INVENTOWNERID_RU ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTGTDID_RULEVEL THEN INVENTDIM.INVENTGTDID_RU ELSE NULL END
        #    ) AS FOUND ON (RESULT.CONFIGID = FOUND.CONFIGID)
        #        AND (RESULT.INVENTSIZEID = FOUND.INVENTSIZEID)
        #        AND (RESULT.INVENTCOLORID = FOUND.INVENTCOLORID)
        #        AND (RESULT.INVENTSTYLEID = FOUND.INVENTSTYLEID)
        #        AND (@LEVEL + -1 < @INVENTSITEIDLEVEL OR RESULT.INVENTSITEID = FOUND.INVENTSITEID)
        #        AND (@LEVEL + -1 < @INVENTLOCATIONIDLEVEL OR RESULT.INVENTLOCATIONID = FOUND.INVENTLOCATIONID)
        #        AND (@LEVEL + -1 < @WMSLOCATIONIDLEVEL OR RESULT.WMSLOCATIONID = FOUND.WMSLOCATIONID)
        #        AND (@LEVEL + -1 < @WMSPALLETIDLEVEL OR RESULT.WMSPALLETID = FOUND.WMSPALLETID)
        #        AND (@LEVEL + -1 < @INVENTSTATUSIDLEVEL OR RESULT.INVENTSTATUSID = FOUND.INVENTSTATUSID)
        #        AND (@LEVEL + -1 < @LICENSEPLATEIDLEVEL OR RESULT.LICENSEPLATEID = FOUND.LICENSEPLATEID)
        #        AND (@LEVEL + -1 < @INVENTBATCHIDLEVEL OR RESULT.INVENTBATCHID = FOUND.INVENTBATCHID)
        #        AND (@LEVEL + -1 < @INVENTSERIALIDLEVEL OR RESULT.INVENTSERIALID = FOUND.INVENTSERIALID)
        #        AND (@LEVEL + -1 < @INVENTPROFILEID_RULEVEL OR RESULT.INVENTPROFILEID_RU = FOUND.INVENTPROFILEID_RU)
        #        AND (@LEVEL + -1 < @INVENTOWNERID_RULEVEL OR RESULT.INVENTOWNERID_RU = FOUND.INVENTOWNERID_RU)
        #        AND (@LEVEL + -1 < @INVENTGTDID_RULEVEL OR RESULT.INVENTGTDID_RU = FOUND.INVENTGTDID_RU)
        #WHERE RESULT.HIERARCHYLEVEL = @LEVEL
        #GROUP BY
        #    RESULT.CONFIGID,
        #    RESULT.INVENTSIZEID,
        #    RESULT.INVENTCOLORID,
        #    RESULT.INVENTSTYLEID,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSITEIDLEVEL THEN RESULT.INVENTSITEID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTLOCATIONIDLEVEL THEN RESULT.INVENTLOCATIONID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @WMSLOCATIONIDLEVEL THEN RESULT.WMSLOCATIONID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @WMSPALLETIDLEVEL THEN RESULT.WMSPALLETID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSTATUSIDLEVEL THEN RESULT.INVENTSTATUSID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @LICENSEPLATEIDLEVEL THEN RESULT.LICENSEPLATEID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTBATCHIDLEVEL THEN RESULT.INVENTBATCHID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTSERIALIDLEVEL THEN RESULT.INVENTSERIALID ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTPROFILEID_RULEVEL THEN RESULT.INVENTPROFILEID_RU ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTOWNERID_RULEVEL THEN RESULT.INVENTOWNERID_RU ELSE NULL END,
        #    CASE WHEN @LEVEL + -1 >= @INVENTGTDID_RULEVEL THEN RESULT.INVENTGTDID_RU ELSE NULL END;
        #
        #
        #    SET @LEVEL = @LEVEL - 1
        #END; -- WHILE
        #
        #SELECT @AVAILPHYSICAL = SUM(CASE WHEN AVAILPHYSICALFOUND < AVAILPHYSICALRESULT THEN AVAILPHYSICALFOUND ELSE AVAILPHYSICALRESULT END),
        #    @AVAILORDERED = SUM(CASE WHEN AVAILORDEREDFOUND < AVAILORDEREDRESULT THEN AVAILORDEREDFOUND ELSE AVAILORDEREDRESULT END)
        #FROM @RESULT
        #WHERE HIERARCHYLEVEL = 0;
        #    */
        #
        #    ret += strFmt('CREATE PROCEDURE [%1]\n', WhsOnHandSPHelper::storedProcedureName(_includeDelta));
        #    ret +=        '    @PARTITION bigint,\n';
        #    ret += strFmt('    %1 int,\n', this.levelExpression(0));
        #    ret += strFmt('    @DATAAREAID %1,\n', this.sqlTypeNameForField(tableNum(InventTable), fieldNum(InventTable, DataAreaId)));
        #    ret += strFmt('    @ITEMID %1,\n', this.sqlTypeNameForField(tableNum(InventTable), fieldNum(InventTable, ItemId)));
        #    if (_includeDelta)
        #    {
        #        ret += strFmt('    %1 %2,\n', #TTSIdParmName, this.sqlTypeNameForField(tableNum(WHSInventReserveDelta), fieldNum(WHSInventReserveDelta, TTSId)));
        #    }
        #
        #    // Generate parameters for supported dimensions
        #    enabledDimsEnumerator.reset();
        #    while (enabledDimsEnumerator.moveNext())
        #    {
        #        [dimFieldId, dimFieldName] = enabledDimsEnumerator.current();
        #        ret += strFmt('    %1 %2 = NULL,\n', this.dimensionParm(dimFieldName), this.sqlTypeNameForField(tableNum(InventDim), dimFieldId));
        #        ret += strFmt('    %1 int = %2,\n', this.dimensionLevelParm(dimFieldName), #INVALIDHIERARCHYLEVEL);
        #    }
        #
        #    // Generate parameters for product dimensions
        #    productDimensionsEnumerator.reset();
        #    while (productDimensionsEnumerator.moveNext())
        #    {
        #        [dimFieldId, dimFieldName] = productDimensionsEnumerator.current();
        #        ret += strFmt('    %1 %2 = NULL,\n', this.dimensionParm(dimFieldName), this.sqlTypeNameForField(tableNum(InventDim), dimFieldId));
        #    }
        #
        #    ret +=        '    -- Output parameters\n';
        #    ret += strFmt('    %1  numeric(32, 16) OUTPUT,\n', #AvailPhysicalParmName);
        #    ret += strFmt('    %1  numeric(32, 16) OUTPUT\n', #AvailOrderedParmName);
        #    ret +=        'AS\n\n';
        #
        #    ret +=        '-- Without nocount on the procedure will return number of affected rows in the result set which is\n';
        #    ret +=        '-- harmful to performance and requires us to adjust field retrieval when calling the stored procedure\n';
        #    ret +=        'SET NOCOUNT ON;\n\n';
        #
        #    ret +=        '-- We need to supress warnings when trying to aggregate NULL columns. This is a valid business scenario\n';
        #    ret +=        '-- and happens e.g. for a non-LP controlled location with batch below item.\n';
        #    ret +=        'SET ANSI_WARNINGS OFF;\n\n';
        #
        #    ret += this.declareResultTableVariable() + '\n';
        #
        #    ret +=        'DECLARE @DIMLIST TABLE(\n';
        #    ret +=        '    INVENTDIMID nvarchar (20) NULL,\n';
        #    ret +=        '    AVAILPHYSICAL numeric(32, 16) NOT NULL,\n';
        #    ret +=        '    AVAILORDERED numeric(32, 16) NOT NULL);\n\n';
        #
        #    ret +=        '-- The algorithm of the stored procedure is as follows:\n';
        #    ret +=        '-- 1. Initialize the @RESULT table with aggregated qtys in WHSInventReserve for the level requested by user\n';
        #    ret +=        '-- 2. Traverse the hierarchy up and for each level:\n';
        #    ret +=        '--     1) Aggregate qtys from previous level in @RESULT grouping by dimensions of current hierarchy level\n';
        #    ret +=        '--     2) Aggregate qtys from current level in WHSInventReserve grouping by dimensions of current hierarchy level\n';
        #    ret +=        '--     3) Select the minimum of the above qtys and insert into @RESULT\n';
        #    ret +=        '-- 3. When hierarchy level 0 is reached, return the sum of aggregated qtys in @RESULT\n\n';
        #
        #    ret +=        '-- In the original query (the one after below query), it is joining InventDim and WHSInventReserve and filter records based on some criteria.\n';
        #    ret +=        '-- But in the execution plan, it doesn\'t seems to generate a good plan (even after UPDATE STATISTICS). In the original execution plan, it always\n';
        #    ret +=        '-- Get records from InventDim first, which returns lot of rows, then only join it to WHSInventReserve and continue filter from there.\n';
        #    ret +=        '-- If we makes it get records from WHSInventReserve first, filter it, then with lesser rows from here, join it to InventDim, it will be quicker.\n';
        #    ret +=        '-- So the optimization is to filter WHSInventResreve first, then use this result at the original query. In the test, this has reduce the execution\n';
        #    ret +=        '-- time from ~250ms to ~7ms\n';
        #    ret +=        '  INSERT INTO @DIMLIST(INVENTDIMID, AVAILPHYSICAL, AVAILORDERED)\n';
        #    ret +=        '  SELECT INVENTDIMID, AVAILPHYSICAL, AVAILORDERED\n';
        #    ret += strFmt('    FROM %1 %2\n', this.whsInventReserveTableName(_includeDelta), inventReserveAlias);
        #    ret += strFmt('   WHERE %1.ITEMID         = @ITEMID\n', inventReserveAlias);
        #    ret += strFmt('     AND %1.PARTITION      = @PARTITION\n', inventReserveAlias);
        #    ret += strFmt('     AND %1.DATAAREAID     = @DATAAREAID\n', inventReserveAlias);
        #    ret += strFmt('     AND %1.HIERARCHYLEVEL = %2\n\n', inventReserveAlias, this.levelExpression(0));
        #
        #    ret +=        '-- Initialize the @RESULT variable.\n';
        #    ret +=        '-- Loads aggregated data for the lowest level requested by user.\n';
        #    ret +=        'INSERT INTO @RESULT (' + this.resultTableVariableFields() + ')\n';
        #
        #    ret += this.selectFromWHSInventReserveStmt(_includeDelta) + '\n';
        #
        #    ret += strFmt('WHILE (%1 > 0)\n', this.levelExpression(0));
        #    ret +=        'BEGIN\n';
        #
        #    ret += strFmt('  DELETE FROM @DIMLIST\n\n');
        #
        #    ret += strFmt('  INSERT INTO @DIMLIST(INVENTDIMID, AVAILPHYSICAL, AVAILORDERED)\n');
        #    ret += strFmt('  SELECT INVENTDIMID, AVAILPHYSICAL, AVAILORDERED\n');
        #    ret += strFmt('    FROM %1 %2\n', this.whsInventReserveTableName(_includeDelta), inventReserveAlias);
        #    ret += strFmt('   WHERE %1.HIERARCHYLEVEL = %2 + -1\n', inventReserveAlias, this.levelExpression(0));
        #    ret += strFmt('     AND %1.ITEMID         = @ITEMID\n', inventReserveAlias);
        #    ret += strFmt('     AND %1.PARTITION      = @PARTITION\n', inventReserveAlias);
        #    ret += strFmt('     AND %1.DATAAREAID     = @DATAAREAID\n\n', inventReserveAlias);
        #
        #    ret += strFmt('    INSERT INTO @RESULT (%1)\n', this.resultTableVariableFields());
        #
        #    ret += this.selectFromResultJoinWHSInventReserveStmt(_includeDelta) + '\n\n';
        #
        #    ret += strFmt('    SET %1 = %1 - 1\n', this.levelExpression(0));
        #    ret +=        'END; -- WHILE\n\n';
        #
        #    ret += strFmt('SELECT %1 = SUM(CASE WHEN %2 < %3 THEN %2 ELSE %3 END),\n',
        #                    #AvailPhysicalParmName,
        #                    #AVAILPHYSICALFOUNDFieldName,
        #                    #AVAILPHYSICALRESULTFieldName);
        #    ret += strFmt('    %1 = SUM(CASE WHEN %2 < %3 THEN %2 ELSE %3 END)\n',
        #                    #AvailOrderedParmName,
        #                    #AVAILORDEREDFOUNDFieldName,
        #                    #AVAILORDEREDRESULTFieldName);
        #    ret +=        'FROM @RESULT\n';
        #    ret += strFmt('WHERE %1 = 0;\n', #HIERARCHYLEVELFieldName);
        #
        #    return ret;
        #}
      ENDSOURCE
      SOURCE #TEC_selectFromWHSInventReserveStmt
        #private str TEC_selectFromWHSInventReserveStmt(boolean _includeDelta)
        #{
        #    str             ret = '';
        #    ListEnumerator  enabledDimsEnumerator = this.enabledDimensions().getEnumerator();
        #    ListEnumerator  productDimsEnumerator = this.productDimensions().getEnumerator();
        #    str             dimFieldName;
        #    str             levelExpr = this.levelExpression(-1);
        #    str             inventReserveAlias = this.whsInventReserveTableAlias(_includeDelta);
        #    str             dimList = 'DIMLIST';
        #    boolean         hasDoneFirstIteration;
        #
        #    ret += strFmt('SELECT SUM(%1.AVAILPHYSICAL),\n', dimList);
        #    ret += strFmt('    SUM(%1.AVAILORDERED),\n', dimList);
        #    ret +=        '    NULL,\n';
        #    ret +=        '    NULL,\n';
        #    ret += strFmt('    %1,\n', this.levelExpression(0));
        #
        #    // Select product dimensions
        #    productDimsEnumerator.reset();
        #    while (productDimsEnumerator.moveNext())
        #    {
        #        dimFieldName = conPeek(productDimsEnumerator.current(), 2);
        #        ret += strFmt('    INVENTDIM.%1,\n', dimFieldName);
        #    }
        #
        #    // Select enabled dimensions
        #    enabledDimsEnumerator.reset();
        #    hasDoneFirstIteration = false;
        #    while (enabledDimsEnumerator.moveNext())
        #    {
        #        if (hasDoneFirstIteration)
        #        {
        #            ret += ',\n';
        #        }
        #
        #        dimFieldName = conPeek(enabledDimsEnumerator.current(), 2);
        #        ret += strFmt('    CASE WHEN %1 >= %2 THEN INVENTDIM.%3 ELSE NULL END',
        #                        this.levelExpression(-1),
        #                        this.dimensionLevelParm(dimFieldName),
        #                        dimFieldName);
        #
        #        hasDoneFirstIteration = true;
        #    }
        #    ret += '\n';
        #
        #    //ret += strFmt('    FROM %1 %2\n', this.whsInventReserveTableName(_includeDelta), inventReserveAlias);
        #    //ret += strFmt('    JOIN INVENTDIM ON INVENTDIM.INVENTDIMID = %1.INVENTDIMID AND INVENTDIM.DATAAREAID = %1.DATAAREAID AND INVENTDIM.PARTITION = %1.PARTITION\n', inventReserveAlias);
        #    //ret += strFmt('    WHERE %1.ITEMID           = @ITEMID\n', inventReserveAlias);
        #    //ret += strFmt('        AND %1.PARTITION      = @PARTITION\n', inventReserveAlias);
        #    //ret += strFmt('        AND %1.DATAAREAID     = @DATAAREAID\n', inventReserveAlias);
        #    //ret += strFmt('        AND %1.HIERARCHYLEVEL = %2\n', inventReserveAlias, this.levelExpression(0));
        #    ret += strFmt('    FROM INVENTDIM INVENTDIM\n');
        #    ret += strFmt('    JOIN @DIMLIST %1\n', dimList);
        #    ret += strFmt('      ON INVENTDIM.INVENTDIMID = %1.INVENTDIMID\n', dimList);
        #    ret += strFmt('    WHERE   INVENTDIM.DATAAREAID = @DATAAREAID\n');
        #    ret += strFmt('        AND INVENTDIM.PARTITION = @PARTITION\n');
        #
        #    // Add product dimensions range
        #    productDimsEnumerator.reset();
        #    while (productDimsEnumerator.moveNext())
        #    {
        #        dimFieldName = conPeek(productDimsEnumerator.current(), 2);
        #        ret += strFmt('        AND (%2 IS NULL OR INVENTDIM.%1 = %2)\n',
        #                        dimFieldName,
        #                        this.dimensionParm(dimFieldName));
        #    }
        #
        #    // Add enabled dimensions range
        #    enabledDimsEnumerator.reset();
        #    while (enabledDimsEnumerator.moveNext())
        #    {
        #        dimFieldName = conPeek(enabledDimsEnumerator.current(), 2);
        #        ret += strFmt('        AND (%1 < %2 OR %3 IS NULL OR %3 = INVENTDIM.%4)\n',
        #                        this.levelExpression(0),
        #                        this.dimensionLevelParm(dimFieldName),
        #                        this.dimensionParm(dimFieldName),
        #                        dimFieldName);
        #    }
        #
        #    ret +=        'GROUP BY\n';
        #
        #    // Add grouping by product dimensions
        #    productDimsEnumerator.reset();
        #    while (productDimsEnumerator.moveNext())
        #    {
        #        dimFieldName = conPeek(productDimsEnumerator.current(), 2);
        #        ret += strFmt('    INVENTDIM.%1,\n', dimFieldName);
        #    }
        #
        #    // Add grouping by enabled dimensions
        #    enabledDimsEnumerator.reset();
        #    hasDoneFirstIteration = false;
        #    while (enabledDimsEnumerator.moveNext())
        #    {
        #        if (hasDoneFirstIteration)
        #        {
        #            ret += ',\n';
        #        }
        #
        #        dimFieldName = conPeek(enabledDimsEnumerator.current(), 2);
        #        ret += strFmt('    CASE WHEN %1 >= %2 THEN INVENTDIM.%3 ELSE NULL END',
        #                        this.levelExpression(-1),
        #                        this.dimensionLevelParm(dimFieldName),
        #                        dimFieldName);
        #
        #        hasDoneFirstIteration = true;
        #    }
        #
        #    ret += ';\n';
        #
        #    return ret;
        #}
      ENDSOURCE
      SOURCE #TEC_selectInnerFromWHSInventReserveStmt
        #private str TEC_selectInnerFromWHSInventReserveStmt(boolean _includeDelta)
        #{
        #    str             ret = '';
        #    ListEnumerator  enabledDimsEnumerator = this.enabledDimensions().getEnumerator();
        #    ListEnumerator  productDimsEnumerator = this.productDimensions().getEnumerator();
        #    str             dimFieldName;
        #    str             levelExpr = this.levelExpression(-1);
        #    str             inventReserveAlias = this.whsInventReserveTableAlias(_includeDelta);
        #    str             dimList = 'DIMLIST';
        #    boolean         hasDoneFirstIteration;
        #
        #    ret += strFmt('SELECT SUM(%1.AVAILPHYSICAL) AS AVAILPHYSICAL,\n', dimList);
        #    ret += strFmt('    SUM(%1.AVAILORDERED) AS AVAILORDERED,\n', dimList);
        #
        #    productDimsEnumerator.reset();
        #    while (productDimsEnumerator.moveNext())
        #    {
        #        dimFieldName = conPeek(productDimsEnumerator.current(), 2);
        #        ret += strFmt('    INVENTDIM.%1 AS %1,\n', dimFieldName);
        #    }
        #
        #    enabledDimsEnumerator.reset();
        #    hasDoneFirstIteration = false;
        #    while (enabledDimsEnumerator.moveNext())
        #    {
        #        if (hasDoneFirstIteration)
        #        {
        #            ret += ',\n';
        #        }
        #
        #        dimFieldName = conPeek(enabledDimsEnumerator.current(), 2);
        #        ret += strFmt('    CASE WHEN %1 >= %2 THEN INVENTDIM.%3 ELSE NULL END AS %3',
        #                        levelExpr,
        #                        this.dimensionLevelParm(dimFieldName),
        #                        dimFieldName);
        #
        #        hasDoneFirstIteration = true;
        #    }
        #    ret += '\n';
        #
        #    //ret += strFmt('FROM %1 %2\n', this.whsInventReserveTableName(includeDelta), inventReserveAlias);
        #    //ret += strFmt('JOIN INVENTDIM ON INVENTDIM.PARTITION = %1.PARTITION\n', inventReserveAlias);
        #    //ret += strFmt('    AND INVENTDIM.DATAAREAID  = %1.DATAAREAID\n', inventReserveAlias);
        #    //ret += strFmt('    AND INVENTDIM.INVENTDIMID = %1.INVENTDIMID\n', inventReserveAlias);
        #    //ret += strFmt('WHERE %1.HIERARCHYLEVEL = %2\n', inventReserveAlias, levelExpr);
        #    //ret += strFmt('    AND %1.ITEMID       = @ITEMID\n', inventReserveAlias);
        #    //ret += strFmt('    AND %1.PARTITION    = @PARTITION\n', inventReserveAlias);
        #    //ret += strFmt('    AND %1.DATAAREAID   = @DATAAREAID\n', inventReserveAlias);
        #    ret += strFmt('FROM INVENTDIM INVENTDIM\n');
        #    ret += strFmt('JOIN @DIMLIST %1\n', dimList);
        #    ret += strFmt('     ON INVENTDIM.PARTITION = @PARTITION\n');
        #    ret += strFmt('    AND INVENTDIM.DATAAREAID  = @DATAAREAID\n');
        #    ret += strFmt('    AND INVENTDIM.INVENTDIMID = DIMLIST.INVENTDIMID\n');
        #    ret +=          'GROUP BY\n';
        #
        #    // Add groupping by product dimensions
        #    productDimsEnumerator.reset();
        #    while (productDimsEnumerator.moveNext())
        #    {
        #        dimFieldName = conPeek(productDimsEnumerator.current(), 2);
        #        ret += strFmt('    INVENTDIM.%1,\n', dimFieldName);
        #    }
        #
        #    // Add groupping by enabled dimensions
        #    enabledDimsEnumerator.reset();
        #    hasDoneFirstIteration = false;
        #    while (enabledDimsEnumerator.moveNext())
        #    {
        #        if (hasDoneFirstIteration)
        #        {
        #            ret += ',\n';
        #        }
        #
        #        dimFieldName = conPeek(enabledDimsEnumerator.current(), 2);
        #        ret += strFmt('    CASE WHEN %1 >= %2 THEN INVENTDIM.%3 ELSE NULL END',
        #                        levelExpr,
        #                        this.dimensionLevelParm(dimFieldName),
        #                        dimFieldName);
        #
        #        hasDoneFirstIteration = true;
        #    }
        #    ret += '\n';
        #
        #    return ret;
        #}
      ENDSOURCE
    ENDMETHODS
  ENDCLASS

***Element: END
=================================================