So, lets take a look at the Interprise search page. You know, the one that looks something like this:

Don’t get me wrong, It functions, and I find that I use this screen all the time over the course of my work day, but I just wish it could answer some questions for me, such as these:
- How much does this product cost again?
- What do I have in stock right now?
- What was the name of that discontinued product again?
Template Changes
So, first, we need to make one change to our template to support this. Don’t fret, it is really easy:
- <script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.6.3.min.js" />
Put the above line to your skin’s template.ascx’s head section. If you don’t know jQuery yet, it is the #1 tool needed to make your site pop. It does not do it for free, but between XmlPackages, SQL, and jQuery, there is almost nothing you can’t do.
Including Discontinued Products In Search Results
Ok, this one is really easy! Create the following SQL Stored Procedure which is just slightly modified from the default aspdnsf_WebSearchInventory procedure:
-
- CREATE PROCEDURE [dbo].[ttx_WebSearchInventoryExt]
- @SearchTerm NVARCHAR(3000),
- @WebSiteCode NVARCHAR(30),
- @LanguageCode NVARCHAR(50),
- @ItemCode NVARCHAR(1000) = NULL,
- @ItemType NVARCHAR(50) = NULL,
- @UnitMeasure NVARCHAR(30) = NULL,
- @CategoryID NVARCHAR (100) = NULL,
- @SectionID NVARCHAR (100) = NULL,
- @ManufacturerID NVARCHAR (100) = NULL,
- @SearchDescriptions VARCHAR(5),
- @CurrentDate DATETIME,
- @ShowDiscontinued BIT = 0
-
- AS
- BEGIN
- SET NOCOUNT ON
-
- SET @SearchTerm = Replace(@SearchTerm,'''','''''')
- SET @SearchTerm = '''%' + @SearchTerm + '%'''
- SET @SearchTerm = Replace(@SearchTerm,'+',' ')
- SET @ItemCode = NULLIF(@ItemCode, NULL)
-
- IF (@ItemType = 'ANY')
- SET @ItemType = '''%'''
- ELSE
- SET @ItemType = '''%' + @ItemType + '%'''
-
- IF (@UnitMeasure = 'ANY')
- SET @UnitMeasure = '''%'''
- ELSE
- SET @UnitMeasure = '%' + @UnitMeasure + '%'''
-
- DECLARE @SearchDescriptionString VARCHAR(1000)
- IF (@SearchDescriptions = '0')
- SET @SearchDescriptionString = ' '
- ELSE
- SET @SearchDescriptionString = ' OR IIWOD.WebDescription LIKE ' + @SearchTerm
-
- DECLARE @JoinUnitMeasureString VARCHAR(1000)
- IF (@UnitMeasure = '''%''')
- SET @JoinUnitMeasureString = ' '
- ELSE
- SET @JoinUnitMeasureString = ' INNER JOIN InventoryUnitMeasure IUM WITH (NOLOCK) ON IUM.ItemCode = A.ItemCode AND ISNULL(UnitMeasureQty,0)>0 AND IUM.UnitMeasureCode LIKE ''' + @UnitMeasure + ''
- DECLARE @EntityBuilderString VARCHAR(1000)
- SET @EntityBuilderString = ''
- IF (@CategoryID <> '0')
- BEGIN
- SET @EntityBuilderString = 'INNER JOIN (SELECT ItemCode FROM InventoryCategory A WITH (NOLOCK) INNER JOIN SystemCategory B WITH (NOLOCK) ON A.CategoryCode = B.CategoryCode WHERE B.Counter = ''' + @CategoryID +
- ''') IC ON IC.ItemCode = A.ItemCode'
- END
- IF (@SectionID <> '0')
- BEGIN
- SET @EntityBuilderString = @EntityBuilderString + ' INNER JOIN (SELECT ItemCode FROM InventoryItemDepartment A WITH (NOLOCK) INNER JOIN InventorySellingDepartment B WITH (NOLOCK) ON A.DepartmentCode = B.DepartmentCode WHERE B.Counter = ''' + @SectionID +
- ''') IIDep ON IIDep.ItemCode = A.ItemCode'
- END
- IF (@ManufacturerID <> '0')
- BEGIN
- SET @EntityBuilderString = @EntityBuilderString + ' INNER JOIN (SELECT A.ManufacturerCode, A.ItemCode FROM InventoryItem A WITH (NOLOCK) INNER JOIN SystemManufacturer B WITH (NOLOCK) ON A.ManufacturerCode = B.ManufacturerCode WHERE B.Counter = ''' + @ManufacturerID +
- ''') SM ON SM.ItemCode = A.ItemCode'
- END
- DECLARE @SQL VARCHAR(8000)
- SET @SQL='SELECT
- A.*, IID.ItemDescription, IIWOD.WebDescription, H.Status, H.RetailPrice, H.UnitsInStock FROM InventoryItem A WITH (NOLOCK)
- LEFT JOIN (SELECT ItemCode FROM InventoryItemPricingDetail B WITH (NOLOCK)
- INNER JOIN SystemCurrency C WITH (NOLOCK) ON B.CurrencyCode = C.CurrencyCode WHERE IsHomeCurrency = 1) D ON A.ItemCode = D.ItemCode
- LEFT JOIN (SELECT TOP 1 ItemKitCode FROM InventoryKitPricingDetail E WITH (NOLOCK)
- INNER JOIN SystemCurrency F WITH (NOLOCK) ON E.CurrencyCode = F.CurrencyCode WHERE IsHomeCurrency = 1) G ON A.ItemCode = G.ItemKitCode
- LEFT JOIN (
-
- SELECT iiapv.ItemCode
- , iiapv.Status
- , iiapv.RetailPrice
- , SUM(istv.UnitsInStock) as UnitsInStock
- FROM [InventoryItemAndPricingView] iiapv WITH (NOLOCK)
- JOIN InventoryMatrixItem imi WITH (NOLOCK) ON iiapv.ItemCode = imi.ItemCode
- JOIN InventoryStockTotalView istv WITH (NOLOCK) ON imi.MatrixItemCode = istv.ItemCode
- WHERE iiapv.LanguageCode = ''' + @LanguageCode + '''
- AND istv.LanguageCode = ''' + @LanguageCode + '''
- AND iiapv.ItemType = ''Matrix Group''
- GROUP BY iiapv.ItemCode
- , iiapv.Status
- , iiapv.RetailPrice
- UNION
- SELECT iiapv.ItemCode
- , iiapv.Status
- , iiapv.RetailPrice
- , istv.UnitsInStock as UnitsInStock
- FROM [InventoryItemAndPricingView] iiapv WITH (NOLOCK)
- JOIN InventoryStockTotalView istv WITH (NOLOCK) ON IIAPV.ItemCode = istv.ItemCode
- WHERE iiapv.LanguageCode = ''' + @LanguageCode + '''
- AND istv.LanguageCode = ''' + @LanguageCode + '''
- AND iiapv.ItemType IN (''Non-Stock'', ''Service'', ''Stock'',''Service'',''Electronic Download'')
- ) H ON H.ItemCode = a.ItemCode
- INNER JOIN InventoryItemDescription IID WITH (NOLOCK) ON IID.ItemCode = A.ItemCode AND IID.LanguageCode = ''' + @LanguageCode + '''
- INNER JOIN InventoryItemWebOption IIWO WITH (NOLOCK) ON IIWO.ItemCode = A.ItemCode AND IIWO.WebsiteCode = ''' + @WebsiteCode + '''
- INNER JOIN InventoryItemWebOptionDescription IIWOD WITH (NOLOCK) ON IIWOD.ItemCode = IIWO.ItemCode AND IIWOD.WebsiteCode = IIWO.WebsiteCode AND IIWOD.LanguageCode = ''' + @LanguageCode + ''' ' + @JoinUnitMeasureString + ' ' + @EntityBuilderString + '
- WHERE (IID.ItemDescription LIKE ' + @SearchTerm + ' OR A.ItemName LIKE ' + @SearchTerm + ' ' + @SearchDescriptionString + ') AND
- ItemType IN (''Non-Stock'', ''Service'', ''Stock'',''Matrix Group'',''Kit'',''Service'',''Electronic Download'', ''Assembly'') AND ''' +
- cast(@CurrentDate as nvarchar(50)) + ''' between isnull(IIWO.StartDate, ''1/1/1900'') AND isnull(IIWO.EndDate, ''12/31/9999'') '
-
- IF (@ShowDiscontinued = 0) BEGIN
- SET @SQL = @SQL + ' AND A.Status = ''A'' '
- END
-
- SET @SQL = @SQL + ' AND IIWO.Published = 1 AND IIWO.CheckOutOption = 0 AND
- ItemType LIKE ' + @ItemType + 'ORDER BY IIWO.IsExclusive DESC, A.ItemName ASC'
-
- EXEC(@SQL)
- --PRINT @SQL
-
- END
Now open your XmlPackages\page.search.xml.config and change the following block:
- <query name="Products" rowElementName="Product" runif="SearchTerm">
- <sql>
- <![CDATA[
- exec aspdnsf_WebSearchInventory @SearchTerm, @WebSiteCode, @LanguageCode, NULL, 'ANY', 'ANY', '0', '0', '0',@SearchDescriptions, @CurrentDate
- ]]>
- </sql>
- <queryparam paramname="@SearchTerm" paramtype="request" requestparamname="SearchTerm" sqlDataType="varchar" defvalue="" validationpattern=""/>
- <queryparam paramname="@WebSiteCode" paramtype="runtime" requestparamname="WebSiteCode" sqlDataType="varchar" defvalue="" validationpattern=""/>
- <queryparam paramname="@LanguageCode" paramtype="runtime" requestparamname="LanguageCode" sqlDataType="varchar" defvalue="" validationpattern=""/>
- <queryparam paramname="@SearchDescriptions" paramtype="runtime" requestparamname="SearchDescriptions" sqlDataType="varchar" defvalue="0" validationpattern=""/>
- <queryparam paramname="@CurrentDate" paramtype="runtime" requestparamname="Date" sqlDataType="datetime" defvalue="0" validationpattern=""/>
- </query>
to this
- <query name="Products" rowElementName="Product" runif="SearchTerm">
- <sql>
- <![CDATA[
- exec ttx_WebSearchInventoryExt @SearchTerm, @WebSiteCode, @LanguageCode, NULL, 'ANY', 'ANY', '0', '0', '0',@SearchDescriptions, @CurrentDate, @ShowDiscontinued
- ]]>
- </sql>
- <queryparam paramname="@SearchTerm" paramtype="request" requestparamname="SearchTerm" sqlDataType="varchar" defvalue="" validationpattern=""/>
- <queryparam paramname="@WebSiteCode" paramtype="runtime" requestparamname="WebSiteCode" sqlDataType="varchar" defvalue="" validationpattern=""/>
- <queryparam paramname="@LanguageCode" paramtype="runtime" requestparamname="LanguageCode" sqlDataType="varchar" defvalue="" validationpattern=""/>
- <queryparam paramname="@SearchDescriptions" paramtype="runtime" requestparamname="SearchDescriptions" sqlDataType="varchar" defvalue="0" validationpattern=""/>
- <queryparam paramname="@CurrentDate" paramtype="runtime" requestparamname="Date" sqlDataType="datetime" defvalue="0" validationpattern=""/>
- <queryparam paramname="@ShowDiscontinued" paramtype="request" requestparamname="ShowDiscontinued" sqlDataType="varchar" defvalue="1" validationpattern=""/>
- </query>
and you should be ready to go. Note, you can add a ‘ShowDiscontinued=0’ or ‘ShowDiscontinued=1’ to show or hide discontinued products.
Labeling Out of Stock or Discontinued Products
So now that we added discontinued products, we can’t tell quickly what is discontinued or not. And while we are at it, why not fix the same issue for stock problems. Let’s throw in some price displays while we are at it.
These are all simple changes to the XmlPackages\page.search.xml.config. Around line 158, we need to add two columns to our Product Results header row:
- <tr>
- <td>
- <b>Notes</b>
- </td>
- <td align="left">
- <b>
- <xsl:value-of select="ise:StringResource('search.aspx.6', $LocaleSetting)" disable-output-escaping="yes"/>
- </b>
- </td>
- <td>
- <b>Price</b>
- </td>
- <td align="center">
- <b>
- <xsl:value-of select="ise:StringResource('AppConfig.CategoryPromptSingular', $LocaleSetting)" disable-output-escaping="yes"/>
- </b>
- </td>
- <xsl:if test="ise:AppConfigBool('Search_ShowManufacturersInResults')='true'">
- <td align="center">
- <b>
- <xsl:value-of select="ise:StringResource('search.aspx.8', $LocaleSetting)" disable-output-escaping="yes"/>
- </b>
- </td>
- </xsl:if>
- </tr>
- <xsl:apply-templates select="/root/Products/Product"/>
And now, in your product template (starting at line ~202), right after the inner-most <tr> element of the product table, add the following:
- <tr>
- <xsl:choose>
- <xsl:when test="Status != 'A' and UnitsInStock = 0">
- <xsl:attribute name="class">searchResult discontinued outOfStock</xsl:attribute>
- </xsl:when>
- <xsl:when test="Status != 'A'">
- <xsl:attribute name="class">searchResult discontinued</xsl:attribute>
- </xsl:when>
- <xsl:when test="UnitsInStock = 0">
- <xsl:attribute name="class">searchResult outOfStock</xsl:attribute>
- </xsl:when>
- <xsl:otherwise>
- <xsl:attribute name="class">searchResult</xsl:attribute>
- </xsl:otherwise>
- </xsl:choose>
add then following columns; a Notes column before the product name column:
- <td width="110px">
- <span style="font-weight:normal;">
- <xsl:choose>
- <xsl:when test="Status != 'A' and UnitsInStock = 0">
- <span style="background: #F2F2F2;">Discontinued</span>
- <br/>
- <span style="background: #F2DCDB;">Out of Stock</span>
- </xsl:when>
- <xsl:when test="Status != 'A'">
- <span style="background: #F2F2F2;">Discontinued</span>
- </xsl:when>
- <xsl:when test="UnitsInStock = 0">
- <span style="background: #F2DCDB;">Out of Stock</span>
- </xsl:when>
- </xsl:choose>
- </span>
- </td>
And the price column after the product name column:
- <td align="right" width="65px" >
- <xsl:choose>
- <xsl:when test="Status != 'A' and UnitsInStock = 0">
- <span style="background: #F2F2F2;">
- $ <xsl:value-of select="format-number(RetailPrice, '#,##0.00')" />
- </span>
- </xsl:when>
- <xsl:when test="Status != 'A'">
- <span style="background: #F2F2F2;">
- $ <xsl:value-of select="format-number(RetailPrice, '#,##0.00')" />
- </span>
- </xsl:when>
- <xsl:when test="UnitsInStock = 0">
- <span style="background: #F2DCDB;">
- $ <xsl:value-of select="format-number(RetailPrice, '#,##0.00')" />
- </span>
- </xsl:when>
- <xsl:otherwise>
- <span >
- $ <xsl:value-of select="format-number(RetailPrice, '#,##0.00')" />
- </span>
- </xsl:otherwise>
- </xsl:choose>
- </td>
Now you have the price and pretty notes for the stock status:

One Word: Checkboxes!!!
Now that we have all that done, we can use some simple jQuery magic to add some checkboxes that hide discontinued and/or out of stock items!
As part of this, we can refactor the search text to a format that is easier to extend (table system that breaks everything out) and makes it easily to match visuals with all our users’ browsers. Replace the the Form element located around Line 76 of XmlPackages\page.search.xml.config with the following:
- <form method="GET" action="search.aspx" id="SearchForm2" name="SearchForm2">
- <table border="0" cellpadding="0" cellspacing="0" width="500px">
- <tr>
- <td colspan="2">
- <span style="color:red;font-weight:bold">
- <xsl:value-of select="/root/QueryString/errormsg" />
- </span>
- </td>
- </tr>
- <tr>
- <td colspan="2">
- <xsl:value-of select="ise:StringResource('search.aspx.3', $LocaleSetting)" disable-output-escaping="yes" /> 
- </td>
- </tr>
- <tr align="left">
- <td width="300px">
- <input type="text" id="SearchTerm" name="SearchTerm" size="50" maxlength="70" value="{$pSearchTerm}" style="width:300px;"></input>
- <br/>
- <xsl:value-of select="ise:GetSearchFormValidatorScript('SearchForm', 'SearchTerm')" disable-output-escaping="yes" />
- </td>
- <td width="200px">
- <input type="submit" value="Search" name="B1" />
- </td>
- </tr>
- <tr align="">
- <td></td>
- <td>
- <input id="ShowDiscontinued" type="checkbox">Show Discontinued</input>
- </td>
- </tr>
- <tr align="">
- <td></td>
- <td>
- <input id="ShowOutOfStock" type="checkbox" checked="checked">Show Out of Stock</input>
- </td>
- </tr>
- </table>
- </form>
which gives us some checkboxes, but now we have to wire them up. Put this script with your <xsl:template match=”/”>
- <xsl:template match="/">
- <xsl:value-of select="ise:Topic('SearchPageHeader')" disable-output-escaping="yes"/>
- <script type="text/javascript">
- function UpdateVisibility()
- {
- $(".searchResult").css("display", "table-row");
-
- if ($("#ShowDiscontinued").is(':checked') == false)
- $(".discontinued").css("display", "none");
-
- if ($("#ShowOutOfStock").is(':checked') == false)
- $(".outOfStock").css("display", "none");
- };
-
- $(document).ready(function()
- {
- $("#ShowDiscontinued").click(UpdateVisibility);
- $("#ShowOutOfStock").click(UpdateVisibility);
- UpdateVisibility();
- });
- </script>
Now, save everything and if you did it right (I know my instructions are not the easiest out there), you have some awesome stuff:

And if you uncheck the “Show Out of Stock” checkbox, all my products go away!

Pretty cool for an hour’s effort huh? Maybe my next post will be table sorting…