Skip to content

Sitecore Commerce-9.3- Add Custom Fields to Customer Address – Account Management

Address on a sitecore commerce customer is saved as “party”. When an address is added or updated, the “AddParties” or “UpdateParties” processor is called under the hood. These processors are in the “Sitecore.Commerce.Engine.Connect.Pipelines.Customers” dll. This processor processes each party by performing a DoAction call using the “AddressDetails” entity view with the new/updated party details mapped to it. 

To save the company name on the “party”, we need to make changes to the files below.

  • Models
    • AddressEditorInputModel
    • AddressEditorItemJsonResult
    • AddressItemJsonResult
  • Processors
    • UpdateParties
    • AddParties
  • Policy
    • PlugIn.Altra.CommerceAuthoring-1.0.0.json
  • Translations/mappings
    • TranslatePartyToEntity
    • TranslateViewToPartyEntity

Models:

We need to add a “Company” property to the “AddressEditorInputModel” to hold the company name value from the address editor form first. We also need to add a property to the “AddressEditorItemJsonResult” model when we get the parties along with the “AddressItemJsonResult” model. This model is used when displaying a list of addresses in the account management.

Processors:

Both the “UpdateParties” & “AddParties” processors get the “AddressDetails” entity view & calls a “DoAction” to save the entity view data. We need to extend these processors and add/update our company property to the entity view.

UpdateParties:

public class CustomUpdateParties : UpdateParties
{
    protected override void ProcessUpdateCommerceParty (
      string customerId,
      CommerceParty party,
      CustomerResult result,
      Sitecore . Commerce . Engine . Container container )
    {
        if ( ! string . IsNullOrWhiteSpace (party . Company ) )
        {
            EntityView entityView = this . GetEntityView (container, customerId, party . ExternalId, "AddressDetails", "EditAddress", (ServiceProviderResult )result ) ;
            if (entityView == null || !entityView . Properties . Any <ViewProperty > ( ) )
                return ;

            var companyProperty = entityView . Properties . FirstOrDefault (p => p . Name == "Company" ) ?? new ViewProperty ( ) ;
            companyProperty . DisplayName = "Company" ;
            companyProperty . Name = "Company" ;
            companyProperty . IsRequired = false ;
            companyProperty . Value = party . Company ;

            entityView . Properties . Add (companyProperty ) ;
            this . DoAction (container, entityView, (ServiceProviderResult )result ) ;
        }
        base . ProcessUpdateCommerceParty (customerId, party, result, container ) ;
    }
}

AddParties:

public class CustomAddParties : AddParties
{
    private readonly string _companyName = "Company" ;

    protected override Party ProcessAddCommerceParty (
      CommerceParty party,
      EntityView view,
      AddPartiesResult result,
      Container container )
    {

        view . Properties . FirstOrDefault <ViewProperty > ( (Func <ViewProperty, bool > ) (p => p . Name . Equals ( "Country", StringComparison . OrdinalIgnoreCase ) ) ) . Value = string . IsNullOrEmpty (party . CountryCode ) ? party . Country : party . CountryCode ;
        CommerceCommand commerceCommand1 = this . DoAction (container, view, (ServiceProviderResult )result ) ;
        if (commerceCommand1 . ResponseCode . Equals ( "error", StringComparison . OrdinalIgnoreCase ) )
            return (Party ) null ;
        EntityView view1 = commerceCommand1 . Models . OfType <EntityView > ( ) . FirstOrDefault <EntityView > ( (Func <EntityView, bool > ) (v => v . Name . Equals (view . Name, StringComparison . OrdinalIgnoreCase ) ) ) ;
        if ( ! string . IsNullOrWhiteSpace (party . Company ) )
        {
            var companyViewProperty = new ViewProperty ( ) ;

            companyViewProperty . DisplayName = _companyName ;
            companyViewProperty . Name = _companyName ;
            companyViewProperty . IsRequired = false ;
            companyViewProperty . Value = party . Company ;

            view1 . Properties . Add (companyViewProperty ) ;
        }
        view1 . Properties . FirstOrDefault <ViewProperty > ( (Func <ViewProperty, bool > ) (p => p . Name . Equals ( "AddressName", StringComparison . OrdinalIgnoreCase ) ) ) . Value = party . Name ;
        view1 . Properties . FirstOrDefault <ViewProperty > ( (Func <ViewProperty, bool > ) (p => p . Name . Equals ( "FirstName", StringComparison . OrdinalIgnoreCase ) ) ) . Value = party . FirstName ;
        view1 . Properties . FirstOrDefault <ViewProperty > ( (Func <ViewProperty, bool > ) (p => p . Name . Equals ( "LastName", StringComparison . OrdinalIgnoreCase ) ) ) . Value = party . LastName ;
        view1 . Properties . FirstOrDefault <ViewProperty > ( (Func <ViewProperty, bool > ) (p => p . Name . Equals ( "Address1", StringComparison . OrdinalIgnoreCase ) ) ) . Value = party . Address1 ;
        view1 . Properties . FirstOrDefault <ViewProperty > ( (Func <ViewProperty, bool > ) (p => p . Name . Equals ( "Address2", StringComparison . OrdinalIgnoreCase ) ) ) . Value = party . Address2 ;
        view1 . Properties . FirstOrDefault <ViewProperty > ( (Func <ViewProperty, bool > ) (p => p . Name . Equals ( "City", StringComparison . OrdinalIgnoreCase ) ) ) . Value = party . City ;
        view1 . Properties . FirstOrDefault <ViewProperty > ( (Func <ViewProperty, bool > ) (p => p . Name . Equals ( "State", StringComparison . OrdinalIgnoreCase ) ) ) . Value = party . RegionCode ?? party . State ;
        view1 . Properties . FirstOrDefault <ViewProperty > ( (Func <ViewProperty, bool > ) (p => p . Name . Equals ( "ZipPostalCode", StringComparison . OrdinalIgnoreCase ) ) ) . Value = party . ZipPostalCode ;
        view1 . Properties . FirstOrDefault <ViewProperty > ( (Func <ViewProperty, bool > ) (p => p . Name . Equals ( "PhoneNumber", StringComparison . OrdinalIgnoreCase ) ) ) . Value = party . PhoneNumber ;
        view1 . Properties . FirstOrDefault <ViewProperty > ( (Func <ViewProperty, bool > ) (p => p . Name . Equals ( "IsPrimary", StringComparison . OrdinalIgnoreCase ) ) ) . Value = party . IsPrimary . ToString ( (IFormatProvider )CultureInfo . InvariantCulture ) ;
        CommerceCommand commerceCommand2 = this . DoAction (container, view1, (ServiceProviderResult )result ) ;
        if (commerceCommand2 . ResponseCode . Equals ( "error", StringComparison . OrdinalIgnoreCase ) )
            return (Party ) null ;
        party . ExternalId = commerceCommand2 . Models . OfType <CustomerAddressAdded > ( ) . FirstOrDefault <CustomerAddressAdded > ( ) . AddressId ;
        return (Party )party ;

    }

Policy: CommerceAuthoring policy: We need to add out company property name to the list of address detail properties.

…..
  "AddressDetailsProperties" : {
          "$type" : "System.Collections.Generic.List`1[[System.String, mscorlib]], mscorlib" ,
          "$values" : [
            "AddressName" ,
            "FirstName" ,
            "LastName" ,
            "Country" ,
            "CountryCode" ,
            "State" ,
            "StateCode" ,
            "City" ,
            "Address1" ,
            "Address2" ,
            "ZipPostalCode" ,
            "IsPrimary" ,
            "PhoneNumber" ,
            "Company"
          ]
        } ,
……

Translations/mappings:

TranslateViewToPartyEntity: We need to extend this processor to map the company value from the “AddressDetails” entity view to the company on the commerce party. This processor is called when we get the party details of a customer.

public class CustomTranslateViewToPartyEntity : TranslateViewToPartyEntity
{
    public CustomTranslateViewToPartyEntity (IEntityFactory entityFactory ) : base (entityFactory )
    {
    }

    protected override void Translate (
      TranslateViewToPartyEntityRequest request,
      EntityView source,
      CommerceParty destination )
    {
        base . Translate (request, source, destination ) ;
        destination . Company = source . Properties . FirstOrDefault <ViewProperty > ( (Func <ViewProperty, bool > )
            (p => p . Name . Equals ( "Company", StringComparison . OrdinalIgnoreCase ) ) ) == null ? string . Empty : source . Properties . FirstOrDefault <ViewProperty > ( (Func <ViewProperty, bool > ) (p => p . Name . Equals ( "Company", StringComparison . OrdinalIgnoreCase ) ) ) . Value ;
    }
}
-TranslatePartyToEntity : This translation processor is invoked when we get the list of addresses on a customer .

public class CustomTranslatePartyToEntity : TranslatePartyToEntity
{
    public CustomTranslatePartyToEntity (IEntityFactory entityFactory ) : base (entityFactory )
    {
    }
    protected override void Translate (
      TranslatePartyToEntityRequest request,
      Sitecore . Commerce . Core . Party source,
      CommerceParty destination )
    {
        base . Translate (request, source, destination ) ;
        destination . Company = source . Organization ;
    }
}