Archive for the ‘Commerce Server’ Category.

Creating a Coupon/Promo Code for a specific product within a virtual catalog.

I’ve found that not always does the Discount Engine correctly pickup product id’s when using virtual catalogs in this fashion. So in order to work around this, we will use a Discount Specific Expression.

 

Frist step is to create the discount from within the Marketing Manager.  Start by navigating from the File –> New –> Discount

 

Click on Next from the wizard introduction screen.

Select the Customer that you would like to track the discount / promo through and click Next.

Select the appropriate Campaign for your discount and click Next

Provide a Discount Name and Basket Display, also make sure that your Start and End dates are correct.

Select Simple Discount Type and then click Next.

Select the product that you want to discount. Click Next.

 

Complete the remaining steps in the wizard, and reopen your discount.

 

image

From here, under the Receive (Award) section change the Product/Category dropdown to Discount Specific Expression.

Hit the select button, A window will open and this is where you will specify the product.

image

Now this is where the magic happens. Simply specify that the Product.ProductID must contain the product Id with out the catalog name.

 

You may now attach a coupon to the discount in order to activate the discount.

 

Steps to verify:

  1. Add the product to the cart.
  2. Apply the Coupon Code.
  3. Verify the discount has been applied.

Commerce Server Sequence Component

Let’s say for instance that each time a user profile was created, you need to create an external record of the account and their initial registration for audit purposes or to alert another system such as CRM or another LOB system that an account has been created.

First we will need to create a new Class Project to house our Operation Sequence Component.

In that project add references to the following (you can do this by unloading the project, opening the .csproj file, and adding the following xml block with the default references)

<Reference Include=”Microsoft.Commerce.Application.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL” />
<Reference Include=”Microsoft.Commerce.Broker, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL” /> <Reference Include=”Microsoft.Commerce.Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL” />
<Reference Include=”Microsoft.Commerce.Contracts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL” />
<Reference Include=”Microsoft.Commerce.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL” />
<Reference Include=”Microsoft.CommerceServer.Catalog, Version=6.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL” />
<Reference Include=”Microsoft.CommerceServer.Runtime, Version=6.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL” />

Create your class, for my example i will be using:

class NotifyUserCreate : OperationSequenceComponent

Make sure your class inherits from OperationSequenceComponent.

Add the following using statements:

using Microsoft.Commerce.Providers.Components;
using Microsoft.Commerce.Contracts.Messages;
using Microsoft.Commerce.Broker;
using Microsoft.CommerceServer.Catalog;
using Microsoft.Commerce.Providers.ContextProviders;

Now for our method we only need to override one of the methods.

public override void ExecuteCreate(CommerceCreateOperation createOperation, OperationCacheDictionary operationCache, CommerceCreateOperationResponse response)
{
}

We will skip the meat of this method for now. Now we will need to modify our ChannelConfiguration.config file.

You will need to find the <MessageHandler> block with the sequence that we are looking to update, which is CommerceCreateOperation_UserProfile. You can search on that string.

In the existing <OperationSequence> block you need to add the following at the end of the sequence.

<Component name=”User Account Creation Notification” type=”Your Full Class Type, Your Assembly, Version=1.0.0.0, Culture=neutral,PublicKeyToken=Your Public Key“/>

Now back to your project, you will need to sign your assembly and deploy it to the GAC.

You can run your new sequence now. It won’t do anything, but you’ll be able to verify your deployment and check your configuration files.

Now lets add some goodies to the method you created above (add the following to your method, or something similar)

string firstName = createOperation.Model.Properties["FirstName"] as string;
string lastName = createOperation.Model.Properties["LastName"] as string;
string email = createOperation.Model.Properties["Email"] as string;
{
// Do your logic here to export to an external system;
}

Now you have completed your operation sequence. Keep in mind you can respond to any of the four operation types (Update/Create/Delete/Query). You can put these sequences in any order, add and remove them from the channel configuration. There is a great deal of extensibility here and an infinite amount of possibility for expansion.

Chasing the elusive (0×80004005): Unspecified error

I have run accross this error many times, i would say about 90% of the time a good old IISRESET clean up the issue. However not this time.

An exception occurred in the ‘ProfilesWebService’ Web service. Exception details follow:

System.Runtime.InteropServices.COMException (0×80004005): Unspecified error

at Microsoft.CommerceServer.Interop.Profiles.ProfileServiceClass.Initialize(String sConfigStore, String sCatalog)
at Microsoft.CommerceServer.Profiles.WebService.ProfilesWebService.get_ProfileServicePIA()
at Microsoft.CommerceServer.Profiles.WebService.ProfilesWebService.NewProfileStorage()
at Microsoft.CommerceServer.Profiles.WebService.ProfilesWebService.CreateProfile(XmlElement& profileXml, Boolean inAdapterContext)

I’m not sure why I did this or how it happened, but the error seemed to be caused and was ultimately fixed when I changed the profile partition data provide from OleDb to Sql Native Client (something which i now know is unsupported). Simply changing it back to the OleDb provider fixed the issue.

You do this through the commerce server manager, by expanding your profiles resource and opening your data sources, you’ll see the partitions listed in the window on the right.

Unusual Exception When Extending Profile System

I started with adding a relationship for customer attributes to the UserObject.

I ran through the regular steps, extending this, processors, translators, etc…. My query opertion was working great, my create also worked well. So then when I went to add the Delete and Update operations… all Exceptions broke loose.

Type : Microsoft.Commerce.Providers.Exceptions.InvalidOperationRequestException, Microsoft.Commerce.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
Message : The operation could not be performed because the search criteria Model is not valid. The search criteria Model must contain exactly one property that maps to a Commerce Server profile property defined as a primary, join, or unique key.

I was very frustrated with this exception as it didn’t point to much, so i continued digging and eventuall found the issue. The issue was in my MetaDefinitions.xml file, and it was a rather simple one:



        
          

            
              
              
              
              
          
        
      

      

What was happening was that my ID field had the improper casing. This was causing the Sequence Component to throw an exception when it went looking to translate the Attribute metadata to the commerce data with the primary key. Fixed the casing and it works like a charm.

View Order Subtotals By Country

Here is a simple query you can run against your transactions database to get some useful information about your international orders.

Select SUM(subtotal) as total, CountryCode, CountryName
From
(
Select Distinct TrackingNumber, Subtotal, CountryCode, CountryName
From (
SELECT     PurchaseOrders.SubTotal, PurchaseOrders.TrackingNumber, OrderAddresses.CountryCode, OrderAddresses.CountryName
FROM         PurchaseOrders LEFT OUTER JOIN
                      Shipments ON PurchaseOrders.OrderGroupId = Shipments.OrderGroupId LEFT OUTER JOIN
                      OrderAddresses ON Shipments.ShippingAddressId = OrderAddresses.OrderAddressId
                      Where OrderAddresses.CountryCode <> 'US'                      
                      ) a
                      ) b
                      group by CountryCode, CountryName
                      order by total desc

It will provide a good executive level overview.

Force an update of a virtual catalog

A simple script to force commerce server to update all the products in virtual catalogs.

A few times in the past i have noticed that the virtual catalog doesn’t always update when a property is updated in the base catalog. Here is a simple solution.

static void Main(string[] args)
{

     CatalogContext context = CatalogContext.Create(new CatalogServiceAgent("http://yourhost/CatalogWebService/CatalogWebService.asmx"));

     CategoryConfiguration config = new CategoryConfiguration();

     config.RecursiveChildProducts = true;

     Category cat = context.GetCategory("Base Catalog", "", "en-us", config);

     foreach (Product p in cat.ChildProducts)
    {

     p["Description"] = DBNull.Value;
     p["DescriptionHtml"] = DBNull.Value;
     p.Save();

     }

     context.RebuildAllCatalogs(true);
}

You’ll notice that i set the description property to DBNull, you will need to include all properties you need updated in a manner similar to this.

Where is my credit card data????

I often get asked by other commerce developers about why sensitive credit card isn’t available for their payment processor during the CheckOut and CreditCard pipeline components. What is happening is very simple, Commerce Server is protecting us from credit card fraud. During a multi step checkout process a Credit Card Payment before the final order is created. Well that’s normal, however this can cause a problem as the CommerceUpdateOperation_Basket sequence will actually remove the credit card number and security code before saving it to the database. There is a simple solution to this issue. The following example is provided and meant for the MCF foundation.

This is a portion of the Order Review Control:

Basket newOrder = this.presenter.SubmitNewOrder();

The presenter referenced here is the OrderReviewPresenter, we will need to extend this. First thing we will do is grab the payment and then put the credit card number and security code back in.

public void UpdateSecureCreditCardData(string creditCardNumber, string cvv2)
    {
      this.LoadBasket();

      Payment payment = BasketHelper.GetCreditCardPayment(this.View.CurrentCart);
      if (payment != null)
      {
        CreditCard creditCard = (CreditCard)payment.PaymentAccount.Target;
        creditCard.CreditCardNumber = creditCardNumber;
        creditCard.CVV2 = cvv2;

        PaymentAccount paymentAccount = payment.PaymentAccount.Target;
        PaymentMethod paymentMethod = paymentAccount.PaymentMethod.Target;
        string paymentMethodId = paymentMethod.Id;

        this.controller.AddCreditCardPaymentUpdateRequest(this.currentBasketName, payment.Id, paymentMethod.Id, creditCard, TypeHelper.GetSafeDecimal(this.View.CurrentCart.Total));
      }
    }

Then we will go back and add the following code just before we save the order.

this.presenter.UpdateSecureCreditCardData(creditCardNumber, cvv2);

Now that we’ve executed our new method, an operation to update the payment adding the credit card data has been queued in the broker, when we call the SubmitNewOrder() method now the credit card information will be available during pipeline execution, notice that it still isn’t there when the operation sequence returns the new order.

Error When Verifying The Catalog Web Service

When you attempt to access your Catalog Web Service,

http://localhost/DefaultSite_CatalogWebService/CatalogWebService.asmx?op=GetServiceVersion

Click the Invoke Button

image

A new window will open displaying the following error:

System.Web.Services.Protocols.SoapException: The catalog Web service does not have write access to the authorizationPolicyPath. Verify that the file exists and that it has the appropriate permissions. —> Microsoft.CommerceServer.ServerFaultException: The catalog Web service does not have write access to the authorizationPolicyPath. Verify that the file exists and that it has the appropriate permissions.
   — End of inner exception stack trace —
   at Microsoft.CommerceServer.Catalog.Internal.WebServiceUtility.PropagateOrLogException(Exception except)
   at Microsoft.CommerceServer.Catalog.Internal.WebServiceUtility.Initialize()
   at Microsoft.CommerceServer.Catalog.Internal.WebServiceUtility.get_CatalogExecutionContext()
   at Microsoft.CommerceServer.Catalog.WebService.CatalogWebService..ctor()

 

What you need to do to fix this, simply grant Modify permissions to the  CatalogAutorizationStore.xml file in the Web Service directory to the user for the Catalog Web Service Application Pool.

For example if the application pool runs as DOMAINCommerceService then you will need to ensure that that user has access to write to the CatalogAuthorizationStore.xml file.

Commerce Server Orders Per Hour

This simple SQL query is great when your business user would like some KPI’s as to real time daily performance.

DECLARE @offset int

SELECT @offset = DATEDIFF(hh, GETUTCDATE(), GETDATE())

SELECT [date], a.[OrderHour], SUM(subtotal) as SubTotal, count(subtotal) as OrderCount
From (
   SELECT subtotal, CAST(FLOOR(CAST(DateAdd(hour,@offset,created) AS FLOAT ))AS DATETIME) as [date], DatePart(hour, created) + @offset as OrderHour
   FROM [DefaultSite_transactions].[dbo].[PurchaseOrders]
  ) as a
Where [date] > DateAdd(day, -1,GETDATE())
group by [date], a.OrderHour
order by [date] desc, orderHour desc

You can wire up a total vs budget to this script, it breaks out the orders by hour based on local time of the SQL server.

Monitor Commerce Server to ensure that you are taking orders.

I use a simple query that runs ever thirty minutes that just returns the number of hours since the last order.  It adjusts for local time. Load this into a stored procedure, write up a little console app that will email you if you haven’t recieved an order in a few hours.  

DECLARE @offset int

SELECT @offset = DATEDIFF(hh, GETUTCDATE(), GETDATE())

SELECT DATEDIFF(hh,(
     SELECT TOP 1 DateAdd(hh, @offset, created)
     FROM [DefaultSite_transactions].[dbo].[PurchaseOrders]
     WHERE subtotal > 0
     Order by created desc),
   GETDATE())