Skip to main content

Notifications

Announcements

No record found.

Supply chain | Supply Chain Management, Commerce
Answered

How to find current store/channelId for Product Searchview to show inventory

Posted on by 1,433

I need to modify the product search view to add the Inventory, Reserved and Ordered columns for the current store which is the channelId for the user logged in. I am trying to use: docs.microsoft.com/.../pos-view-extension

I went through all the view extension examples under "..\RetailSDK\POS\Extensions\SampleExtensions\ViewExtensions" but cannot find an example.

If I look at example CustomInventoryByStoreListColumns.ts then it seems it is just a list of stores with their quantities. If I look on POS on the inventory lookup page, after one entered a product on the product search on the side, it does give me a current store quantity.

6170.InventoryLookupview.jpg

But the above is standard code and thus locked down. So I have no idea how it was done.

So for the channelId of the user that is logged in, I need to show the following for each product in the searchview:

7446.ProdInventoryLookupview.jpg

 

Any ideas on how to accomplish what I need to do? I am new to Retail development so any help is much appreciated. 

Have to say I have a cloud-hosted development environment with D365FO v10.0.6 PU30 and I am customizing CPOS incase that makes a difference.

  • Consultancy Profile Picture
    Consultancy 5 on at
    RE: How to find current store/channelId for Product Searchview to show inventory

    How can i request the Crt to fetch the query data.

    I am trying to customise new  .ts

    I import as per requirement  I try so many sample but I can't expecte it .

  • Oksana Kovaliova Profile Picture
    Oksana Kovaliova 3,595 on at
    RE: How to find current store/channelId for Product Searchview to show inventory

    Great you were able to solve your task!

    Thank you for sharing the solution with Community

  • Retha Profile Picture
    Retha 1,433 on at
    RE: How to find current store/channelId for Product Searchview to show inventory

    Thank you SO MUCH for your help Oksana. I would not have been able to do this without you.

    Here is my final code and it is working !!! I thought to put it here incase there are others that is new to Retail development like me and who has to learn on the fly.

    References added to my project:

    7571.pastedimage1577724266664v3.png

    Make sure the namespace used inside the classes are the same as the name of the project.

    Trigger class:

    ***

    namespace ProdSearchViewCRT

    {

       using System;

       using System.Collections.Generic;

       using Microsoft.Dynamics.Commerce.Runtime;

       using Microsoft.Dynamics.Commerce.Runtime.Data;

       using Microsoft.Dynamics.Commerce.Runtime.DataModel;

       using Microsoft.Dynamics.Commerce.Runtime.DataServices.Messages;

       using Microsoft.Dynamics.Commerce.Runtime.Messages;

       using Microsoft.Dynamics.Commerce.Runtime.Services.Messages;

       /// <summary>

       /// Class that implements a post trigger for the GetProductSearchResultsDataRequest request type.

       /// </summary>

       public class GetProductSearchTriggers : IRequestTrigger

       {

           /// <summary>

           /// Gets the supported requests for this trigger.

           /// </summary>

           public IEnumerable<Type> SupportedRequestTypes

           {

               get

               {

                    return new[] { typeof(GetProductSearchResultsDataRequest) };

               }

           }

           /// <summary>

           /// Post trigger code to retrieve extension properties.

           /// </summary>

           /// <param name="request">The request.</param>

           /// <param name="response">The response.</param>

           public void OnExecuted(Request request, Response response)

           {

               ThrowIf.Null(request, "request");

               ThrowIf.Null(response, "response");

               var productsList = ((EntityDataServiceResponse<ProductSearchResult>)response);

               if (productsList == null)

               {

                   return;

               }

               foreach (ProductSearchResult product in productsList)

               {

                   CommerceProperty availPhys = new CommerceProperty();

                   availPhys.Key = "Inventory";

                   var query = new SqlPagedQuery(QueryResultSettings.AllRecords)

                   {

                       DatabaseSchema = "ext",

                       Select = new ColumnSet("RECID", "AVAILPHYSICAL"),

                       From = "TMCITEMSTOREAVAILABILITYVIEW",

                       Where = "ITEMID = @itemId AND INVENTLOCATIONID = @channelLocation AND DATAAREAID = @companyId"

                   };

                   query.Parameters["@itemId"] = product.ItemId;

                   query.Parameters["@channelLocation"] = request.RequestContext.GetChannelConfiguration().InventLocation;

                   query.Parameters["@companyId"] = request.RequestContext.GetChannelConfiguration().InventLocationDataAreaId;

                   decimal qty = 0;

                   using (var databaseContext = new DatabaseContext(request))

                   {

                       PagedResult<ProductAvailQty> extensions = databaseContext.ReadEntity<ProductAvailQty>(query);

                       var resultObj = extensions.Results;

                       foreach (ProductAvailQty obj in extensions.Results)

                       {

                           qty += obj.AvailQty;

                       }

                       // Remove the trailing zeroes where applicable e.g. 5.00000 will be displayed as 5 and 5.01000 will be displayed as 5.01

                       availPhys.Value = qty.ToString("0.####");

                   }

                   product.ExtensionProperties.Add(availPhys);

               }

           }

           public void OnExecuting(Request request)

           {

               if (request == null)

               {

                   throw new ArgumentNullException("request");

               }

           }

       }

    }

    ***

    My Commerce Entity:

    ***

    namespace ProdSearchViewCRT

    {

       using System.Runtime.Serialization;

       using Microsoft.Dynamics.Commerce.Runtime.ComponentModel.DataAnnotations;

       using Microsoft.Dynamics.Commerce.Runtime.DataModel;

       using System;

       class ProductAvailQty : CommerceEntity

       {

           //

           // Summary:

           //     Initializes a new instance of the TMC.ProductAvailQtyy

           //     class.

           public ProductAvailQty()

               : base("ProductAvailQty")

           {

           }

           /// <summary>

           /// Gets or sets the physical available quantity.

           /// </summary>

           [DataMember]

           [Column("AVAILPHYSICAL")]

           public Decimal AvailQty

           {

               get

               {

                   return (Decimal)(this["AVAILPHYSICAL"] ?? (object)Decimal.Zero);

               }

               set

               {

                   this["AVAILPHYSICAL"] = (object)value;

               }

           }

           /// <summary>

           /// Gets or sets the id.

           /// </summary>

           [DataMember]

           [Column("RECID")]

           [Key]

           public long RecordId

           {

               get

               {

                   return (long)(this["RECID"] ?? (object)0L);

               }

               internal set

               {

                   this["RECID"] = (object)value;

               }

           }

       }

    }

    ***

    To reference it in CloudPos:

    TypeScript file:

    ***

    import { IProductSearchColumn } from "PosApi/Extend/Views/SearchView";

    import { ICustomColumnsContext } from "PosApi/Extend/Views/CustomListColumns";

    import { CurrencyFormatter } from "PosApi/Consume/Formatters";

    import { ProxyEntities } from "PosApi/Entities";

    import { ObjectExtensions, StringExtensions } from "PosApi/TypeExtensions";

    /**

        * Gets the property value given the column name.

        * @param {ProxyEntities.CommerceProperty[]} extensionProperties The extension properties collection.

        * @param {string} column The column name of the property value to be retrieved.

        * @returns The property value.

        */

    export default (context: ICustomColumnsContext): IProductSearchColumn[] => {

       return [

           {

               title: "Item Id",

               computeValue: (row: ProxyEntities.ProductSearchResult): string => { return row.ItemId; },

               ratio: 20,

               collapseOrder: 3,

               minWidth: 120

           }, {

               title: "Name",

               computeValue: (row: ProxyEntities.ProductSearchResult): string => { return row.Name; },

               ratio: 40,

               collapseOrder: 2,

               minWidth: 200

           }, {

               title: "Inventory",

               computeValue: (row: ProxyEntities.ProductSearchResult): string => {

                   if (!ObjectExtensions.isNullOrUndefined(row.ExtensionProperties)) {

                       let inventProperties: ProxyEntities.CommerceProperty[] = row.ExtensionProperties.filter(

                           (value: ProxyEntities.CommerceProperty): boolean => {return value.Key === "Inventory";});

                            return inventProperties.length > 0 ? inventProperties[0].Value.StringValue : StringExtensions.EMPTY;

                           }

                           return StringExtensions.EMPTY;

               },

               ratio: 20,

               collapseOrder: 4,

               minWidth: 100,

               isRightAligned: true

           }, {

               title: "Price",

               computeValue: (row: ProxyEntities.ProductSearchResult): string => { return CurrencyFormatter.toCurrency(row.Price); },

               ratio: 20,

               collapseOrder: 1,

               minWidth: 100,

               isRightAligned: true

           }

       ];

    };

    ***

    And the manifest.json:

    "extend": {

         "views": {

           "SearchView": {

             "productListConfiguration": { "modulePath": "SearchViewExtensions/ProductSearchView/ProductSearchViewColumns" }

           }

         }

    4666.pastedimage1577723903892v1.png

    Because of a previous custom, I already had TMCPosExtension in Extensions.json and tsconfig.json

    0116.pastedimage1577724099832v2.png

  • Retha Profile Picture
    Retha 1,433 on at
    RE: How to find current store/channelId for Product Searchview to show inventory

    We do not make use of variants so it should have shown only 1 line

  • Oksana Kovaliova Profile Picture
    Oksana Kovaliova 3,595 on at
    RE: How to find current store/channelId for Product Searchview to show inventory

    Hi, did you consider that you can have different product dimensions for the same product? Availability is 'per inventdim', so if product 'Jeans' have 3 product variants - you will have 3 records in the view

  • Retha Profile Picture
    Retha 1,433 on at
    RE: How to find current store/channelId for Product Searchview to show inventory

    Thank you Oksana, I finally found the Product Availability function you were talking about. I ran it and then job 1130 and now I have data in [ax].RetailInventAvailability table.

    Here is the SQL for TMCITEMSTOREAVAILABILITYVIEW.

    ***

    SET ANSI_NULLS ON

    GO

    SET QUOTED_IDENTIFIER ON

    GO

    CREATE VIEW [ext].[TMCITEMSTOREAVAILABILITYVIEW] AS

    (

       SELECT

           RIAV.RECID

           ,RIAV.ITEMID

           ,WID.INVENTDIMID AS WAREHOUSEINVENTDIMID

           ,WID.INVENTSITEID

           ,WID.INVENTLOCATIONID

           ,RIAV.AVAILPHYSICAL

           ,ITM.UNITID

           ,RIAV.DATAAREAID

       FROM [crt].RETAILINVENTAVAILABILITYVIEW RIAV

       JOIN [ax].INVENTTABLEMODULE ITM ON ITM.ITEMID = RIAV.ITEMID AND ITM.DATAAREAID = RIAV.DATAAREAID AND ITM.MODULETYPE = 0

       JOIN [ax].INVENTDIM WID ON WID.INVENTDIMID = RIAV.INVENTDIMID AND WID.DATAAREAID = RIAV.DATAAREAID

    )

    GO

    ***

    When I run a select on it in SSMS it is picking up data. So I don't think the problem is with the view.

    4380.pastedimage1577203197373v1.png

    As you can see it picks up the same number of records that is in the [ax].RetailInventAvailability table

  • Verified answer
    Oksana Kovaliova Profile Picture
    Oksana Kovaliova 3,595 on at
    RE: How to find current store/channelId for Product Searchview to show inventory

    Please, check below picture - I am talking about 'Product availability' job - it is not 1130, but a separate job that you can start as batch - it will be populating ax.retailinventavailability table. 1130 job can just send the data prepared be 'Product availability' batch job

    1830.Capture.PNG

    To see what is wrong in your code - please, share SQL for TMCITEMSTOREAVAILABILITYVIEW

  • Retha Profile Picture
    Retha 1,433 on at
    RE: How to find current store/channelId for Product Searchview to show inventory

    Hi Ram, I first need to try Oksana's way because the reason why we switch for the order desk from D365 full client to Retail thin client is because the whole sales order creation, completion and advanced product search is very slow on the full client.

    So it won't help if I make the product search slow again by calling realtime

  • Retha Profile Picture
    Retha 1,433 on at
    RE: How to find current store/channelId for Product Searchview to show inventory

    Oksana with 'Calculate product availability' I assume you are talking about the distribution schedule 1130? Because it is on my Dev box there isn't a lot of change in the inventory. So I ran it a few times by clicking on the "Run now" button for job 1130 in the disctribution schedule. That should have populated the RetailInventAvailability table if I understood you correctly.

    However it is not doing anything. When I click on History it just states "No data".

    2161.pastedimage1577140836935v1.png

    I couldn't find anything under Microsoft.Docs that indicate there is another "Calculate product availability" P-job.

    I then in the end manually through SSMS added 1 record for a specific product in [ax].RetailInventAvailability table.

    However my code doesn't even show for this specific item the availPhysical.

    I am trying to debug my CRT code. However I have no idea how. I will log another question on that so that this topic doesn't contain 2 issues' answers.

    I changed my CRT as follow:

    ***

    namespace TMC
    {
        using System;
        using System.Collections.Generic;
        using Microsoft.Dynamics.Commerce.Runtime;
        using Microsoft.Dynamics.Commerce.Runtime.Data;
        using Microsoft.Dynamics.Commerce.Runtime.DataModel;
        using Microsoft.Dynamics.Commerce.Runtime.DataServices.Messages;
        using Microsoft.Dynamics.Commerce.Runtime.Messages;
        using Microsoft.Dynamics.Commerce.Runtime.Services.Messages;

        /// <summary>
        /// Class that implements a post trigger for the GetProductSearchResultsDataRequest request type.
        /// </summary>
        public class GetProductSearchTriggers : IRequestTrigger
        {
            /// <summary>
            /// Gets the supported requests for this trigger.
            /// </summary>
            public IEnumerable<Type> SupportedRequestTypes
            {
                get
                {
                    /*
                    return new[]
                        {
                            typeof(GetProductSearchResultsByProductIdsDataRequest),
                            typeof(GetProductSearchResultsDataRequest),
                        };
                        */
                    return new[] { typeof(GetProductSearchResultsDataRequest) };
                }
            }
            /// <summary>
            /// Post trigger code to retrieve extension properties.
            /// </summary>
            /// <param name="request">The request.</param>
            /// <param name="response">The response.</param>
            public void OnExecuted(Request request, Response response)
            {
                ThrowIf.Null(request, "request");
                ThrowIf.Null(response, "response");

                var productsList = ((EntityDataServiceResponse<ProductSearchResult>)response);
                if (productsList == null)
                {
                    return;
                }
                foreach (ProductSearchResult product in productsList)
                {
                    CommerceProperty availPhys = new CommerceProperty();
                    availPhys.Key = "Inventory";
                    var query = new SqlPagedQuery(QueryResultSettings.AllRecords)
                    {
                        DatabaseSchema = "ext",
                        Select = new ColumnSet("RECID", "AVAILPHYSICAL"),
                        From = "TMCITEMSTOREAVAILABILITYVIEW",
                        Where = "ITEMID = @itemId AND INVENTLOCATIONID = @channelLocation AND DATAAREAID = @companyId"
                    };
                    query.Parameters["@itemId"] = product.ItemId;
                    query.Parameters["@channelLocation"] = request.RequestContext.GetChannelConfiguration().InventLocation;
                    query.Parameters["@companyId"] = request.RequestContext.GetChannelConfiguration().InventLocationDataAreaId;
                    var qty = 0;
                    using (var databaseContext = new DatabaseContext(request))
                    {
                        PagedResult<TMC.ProductAvailQty> extensions = databaseContext.ReadEntity<TMC.ProductAvailQty>(query);
                        var resultObj = extensions.Results;
                        foreach (TMC.ProductAvailQty obj in extensions.Results)
                        {
                            qty += obj.AvailQty;
                        }
                        availPhys.Value = qty.ToString();
                    }
                    
                    product.ExtensionProperties.Add(availPhys);
                   
                }
            }
           public void OnExecuting(Request request)
            {
                if (request == null)
                {
                    throw new ArgumentNullException("request");
                }
            }

        }
    }

    ***

    Note that I had to switch to a custom view because the standard view: ITEMAVAILABILITYVIEW, repeats the 1 line in [ax].RetailInventAvailability 3 times if I run a select on it in SSMS.

    View RETAILINVENTAVAILABILITYVIEW on the other hand shows the entry only once which is correct but I cannot call this view because it doesn't give me the option to run it for a specific store.. My custom view is just a copy of ITEMAVAILABILITYVIEW but without the join that cause the repetition.

    Can you see anything wrong in my code above?

  • ram shenkar Profile Picture
    ram shenkar 515 on at
    RE: How to find current store/channelId for Product Searchview to show inventory

    Hi

    You can use the existing request to get the inventory of any product. You have to just pass the Item Id and variant (if applicable)

    GetStoreAvailabilityRealtimeRequest

    Remember, As Oksana stated earlier it may impact performanace little bit.

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

December Spotlight Star - Muhammad Affan

Congratulations to a top community star!

Top 10 leaders for November!

Congratulations to our November super stars!

Tips for Writing Effective Suggested Answers

Best practices for providing successful forum answers ✍️

Leaderboard

#1
André Arnaud de Calavon Profile Picture

André Arnaud de Cal... 291,269 Super User 2024 Season 2

#2
Martin Dráb Profile Picture

Martin Dráb 230,198 Most Valuable Professional

#3
nmaenpaa Profile Picture

nmaenpaa 101,156

Leaderboard

Featured topics

Product updates

Dynamics 365 release plans