Hi Oksana, here is what I did.
1) CRT side:
a) Post trigger:
I add the extension property here. note I did not create a channel DB table because I do not want the available physical quantity to persist in the DB. I just want to show it in the grid in POS. I also call view: ITEMAVAILABILITYVIEW because I need to show the quantity for the existing store only.
****
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;
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 product = ((SingleEntityDataServiceResponse<Product>)response).Entity;
if (product == null)
{
return;
}
CommerceProperty availPhys = new CommerceProperty();
availPhys.Key = "Inventory";
var query = new SqlPagedQuery(QueryResultSettings.AllRecords)
{
DatabaseSchema = "crt",
Select = new ColumnSet("RECID", "AVAILPHYSICAL"),
From = "ITEMAVAILABILITYVIEW",
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;
using (var databaseContext = new DatabaseContext(request))
{
PagedResult<TMC.ProductAvailQty> extensions = databaseContext.ReadEntity<TMC.ProductAvailQty>(query);
var resultObj = extensions.Results;
var qty = 0;
foreach (TMC.ProductAvailQty obj in extensions.Results)
{
qty += obj.AvailQty;
}
availPhys.Value = qty.ToString();
}
product.ExtensionProperties.Add(availPhys);
}
public void OnExecuting(Request request)
{
throw new NotImplementedException();
}
}
}
****
b) I then created the following class to have a commerce entity to put the data in when SQL returns the quantities. I couldn't find an example where the call the channel DB did not make use of a persisted table in the channel DB.
namespace TMC
{
using System.Runtime.Serialization;
using Microsoft.Dynamics.Commerce.Runtime.ComponentModel.DataAnnotations;
using Microsoft.Dynamics.Commerce.Runtime.DataModel;
class ProductAvailQty : CommerceEntity
{
private const string AvailColumn = "AVAILPHYSICAL";
private const string IdColumn = "RECID";
//
// 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(AvailColumn)]
public int AvailQty
{
get { return (int)this[AvailColumn]; }
set { this[AvailColumn] = value; }
}
/// <summary>
/// Gets or sets the id.
/// </summary>
[Key]
[DataMember]
[Column(IdColumn)]
public long Id
{
get { return (long)this[IdColumn]; }
set { this[IdColumn] = value; }
}
}
}
On a side note, the way I populate the above commerce entity, will it only contain data for the specific user during the scope of the request or will it contain other users' data as well meaning that when multiple users access the product search that this entity will not contain the quantity just for their search request but also that of the other users and thus showing the wrong quantity.
c) I have built my project and it created a DLL for me. I placed the dll in K:\\RetailServer\WebRoot\bin\Ext\
Dll name: "TMC_6433_ProdSearchInventValuesCRT.dll"
d) The RetailSDK that I develop in, is on the C-drive. And I have added my DLL to the CommerceRuntime.Ext.Config:
<add source="assembly" value="TMC_6433_ProdSearchInventValuesCRT" />
2) POS side:
On the CloudPos solution, under Pos.Extensions, I created the directory structure: TMCPosExtensions\SearchViewextensions\ProductSearchView.
In here I have created a ts-file:
a) TS-file with the Inventory custom column. I used CustomProductSearchColumns.ts as the base and used CustomOrderListColumns.ts as an example how to read extension properties to populate the column value. Maybe I did something wrong here but ti compiled without errors:
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_CUSTOMIZED",
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
}
];
};
b) Then in the manifest.json file under TMCPosExtensions I already has 1 custom which is working and showing when I run CloudPOS. So I just added the view extension. I used the manifest.json under Pos\Extensions\SampleExtensions\ as an example.
"SearchView": {
"productListConfiguration": { "modulePath": "SearchViewExtensions/ProductSearchView/ProductSearchViewColumns" }
}
c) Then with my previous custom I added already TMCPosExtensions to the extensions.json which is under Pos.Extensions:
{
"extensionPackages": [
{
"baseUrl": "TMCPosExtensions"
}
]
}
d) Lastly I added previously my extension also to tsconfig.json under Exclude:
"exclude": [
//"TMCPosExtensions",
"AuditEventSignatureSample",
"B2BSample",
"CreateCashManagementTransactionSample",
"CreateTenderCountingTransactionSample",
"CustomerSearchWithAttributesSample",
"DualDisplaySample",
"FiscalRegisterSample",
"PaymentSample",
"PrefixUserIdSample",
"PromotionsSample",
"SalesOrdersSample",
"SalesTransactionSignatureNorway",
"SalesTransactionSignatureSample",
"SalesTransBuildNumberSample",
"SampleExtensions",
"SampleExtensions2",
"SequentialSignature",
"StoreHoursSample",
"SuspendTransactionReceiptSample",
"WarrantyAndReturnSample"
],
Output:

I am very excited to see my column but it has no values. It might be the view I'm using so I logged into SSMS and ran:
select *
from [crt].ITEMAVAILABILITYVIEW
It returned nothing. I then ran a for each on [ax].RETAILINVENTAVAILABILITY and it also returned nothing.
I ran in D365 under Retail in the distribution schedule job 1130 using button "run now" and it looks like it is doing nothing because it returns very quickly. I even ran it in a batch and it finished the job in 2 seconds. So it is clearly not running. So how on earth do I get data into that channel DB on my Dev box?
There are products in [ax]/EcoResProduct and inventdimIds in [ax].InventDim. I ran job 1040 and it waits awhile before it returns to the screen which means it is running. IT is just job 1130 that has an issue it seems.