While upgrading a Sitecore 10.0 Commerce solution to the new Sitecore 10.2 version, we found out that Sitecore has written a US-centred piece of code, during the validation of the user of a new shopping cart.
So if you live outside the US and have this error message when creating a new shopping cart in XC 10.2, this article is for you !
CD log:
ERROR DoCommand Failed:Invalid or missing value for property 'State Code'.
ERROR DoCommand Failed:Transaction failed and rolled back.
Engine shops log:
WARN CtxMsg.ValidationError.InvalidOrMissingPropertyValue: Text=Invalid or missing value for property 'StateCode'.|Shopper=xx|Shop=yy|Correlation=zz
WARN CtxMsg.ValidationError.InvalidOrMissingPropertyValue: Text=Invalid or missing value for property ' StateCode'. Should be in the range ' 1 - 20'.|Shopper=xx|Shop=yy|Correlation=zz
ERROR PipelineAbort:Fulfillment.block.ValidateCartFulfillmentParty: Invalid shipping party
The reason why you get this error is because the default “shipping party” for your cart doesn’t have a StateCode. In our case, living in Sweden, the shipping party had every bit of the adress correct, but the validation pipeline tried to get a StateCode from the list of the swedish regions, which are irrelevant in a swedish adress.
The pipeline validation function get the correct country (sweden), the corresponding object (of type Sitecore.Commerce.Core.Country) has an object of type Sitecore.Commerce.Core.RegionComponent. This region component in its turn does not have a list of StatesProvinces, as a country like the US should have. In that case, the validation function can’t find a valid StateCode, and therefore fails miserabily.
How to fix that ?
There are two “warnings” (actually errors) in the validation pipeline:
- Invalid or missing value for property ‘StateCode’
- StateCode Should be in the range ‘ 1 – 20’
The Validation Policy
The StateCode string length is defined in PlugIn.Validation.PolicySet-1.0.0.json, so it is easy to fix this error so the StateCode can be empty. Locate this file (usually from the root of the engine solution: /wwwroot/data/Environments). When defining Sitecore.Commerce.Core.Party, just change:
{
"$type": "Sitecore.Commerce.Core.ValidationAttributes, Sitecore.Commerce.Core",
"Name": "StateCode",
"MaxLength": 20,
"MinLength": 1
}
by:
{
"$type": "Sitecore.Commerce.Core.ValidationAttributes, Sitecore.Commerce.Core",
"Name": "StateCode",
"MaxLength": 20
}
Don’t forget to bootstrap your server, and this is done !
The Validation Pipeline
Unfortunately, just changing the policy is not enough, because the validation pipeline is trying to get a value for StateCode and fails if it doesn’t get one.
We need to change the pipeline block Sitecore.Commerce.Core.ValidatePartyBlock from the IValidatePartyPipeline pipeline.
Create a new class for your custom ValidatePartyBlock.
We just need to check if the shipping party’s country has StateProvinces or not. If not, just return the party instead of trying to define a good StateCode.
In the OOTB code (you can dissasemble Sitecore.Commerce.Core.dll with for ex JetBrains dotPeek, and find the code for Sitecore.Commerce.Core.ValidatePartyBlock) for the function ValidateCountryState(Party, CommercePipelineExecutionContext), after building the Sitecore.Commerce.Core.Country object, the following check is done:
if (!country.HasComponent<RegionComponent>())
return party;
if (string.IsNullOrEmpty(party.StateCode))
...
We need to add to check if this country has provinces or not. The new code will look like that, leaving the rest of the validating function unchanged:
bool countryHasProvinces = country.EntityComponents.OfType<RegionComponent>().FirstOrDefault()?.StatesProvinces?.Count() > 0;
if (!country.HasComponent<RegionComponent>() || !countryHasProvinces)
return party;
if (string.IsNullOrEmpty(party.StateCode))
...
That’s it ! Now you can replace the old pipeline block by your custom one in ConfigureSitecore.cs:
services.Sitecore().Pipelines(config => config
.ConfigurePipeline<IValidatePartyPipeline>(configure =>
configure.Replace<Sitecore.Commerce.Core.ValidatePartyBlock, My.Commerce.Plugin.Carts.Pipelines.Blocks.CustomValidatePartyBlock>()
)
Have fun !