URL for Commerce Categories Contains Incorrect Delimiters

In some implementations of SCA, a URL that uses the Item Search API to access a commerce category contains incorrect delimiters. As a result, accessing pages for a specific category in a SuiteCommerce store returns a 404 error code, even if items for the category exist. For example, a URL might contain %3F instead of the question mark (?) character or %2F instead of forward slash (/).

This patch contains updated code for the FacetsTranslator object constructor. To implement this patch, you must override the Facets.Translator.js JavaScript file in the Facets module. You can download the code samples described in this procedure here: IncorrectDelimetersforCommerceCategories.zip

Note:

In general, best practice is to extend JavaScript using the JavaScript prototype object. This improves the chances that your customizations continue to work when migrating to a newer version of SuiteCommerce Advanced. However, this patch requires you to modify a file that you cannot extend, and you must use a custom module to override the existing module file. For more information, see Develop Your SCA Customization.

The following procedure shows how to override the Facets.Translator.js file, located in the Facets module.

Step 1: Create the Facets.Translation.js Override File

  1. To override the Facets.Translator.js file, located in the Facets module, create a directory to store your custom module.

  2. Open this directory and create a custom module to maintain your customizations.

    Give this module a name similar to the module being customized.

    For example:

    Modules/extensions/FacetsExtension@1.0.0

  3. In your new module, create a subdirectory called JavaScript.

    For example:

    Modules/extensions/FacetsExtension@1.0.0/JavaScript

  4. Copy the following file into the new directory:

    Modules/suitecommerce/Facets@X.Y.Z/JavaScript/Facets.Translator.js

    In this case, X.Y.Z represents the version of the module in your implementation of SuiteCommerce Advanced.

  5. Open your new Facets.Translator.js and make the following change:

    Locate the JavaScript code for the FacetsTranslator constructor in your new file:

                      //@constructor @param {Array}facets @param {Object} options @param {Object} configuration
      function FacetsTranslator (facets, options, configuration, category)
      {
        // Enforces new
        if (!(this instanceof FacetsTranslator))
        {
          return new FacetsTranslator(facets, options, configuration, category);
        }
    
        ...
    
        else if (facets)
        {
          // It's an API option object
          this.parseOptions(facets);
        }
      } 
    
                  

    Replace these lines with the following code:

                             //@constructor @param {Array}facets @param {Object} options @param {Object} configuration
      function FacetsTranslator (facets, options, configuration, category)
      {
        // Enforces new
        if (!(this instanceof FacetsTranslator))
        {
          return new FacetsTranslator(facets, options, configuration, category);
        }
    
        // Facets go Here
        this.facets = [];
        
        // Other options like page, view, etc. goes here
        this.options = {};
        
        // This is an object that must contain a fallbackUrl and a lists of facet configurations
        this.configuration = configuration || default_config;
    
        // Get the facets that are in the sitesettings but not in the config.
        // These facets will get a default config (max, behavior, etc.) - Facets.Translator
        // Include facet aliases to be conisdered as a possible route
        var facets_data = Configuration.get('siteSettings.facetfield')
        , facets_to_include = [];
    
        _.each(facets_data, function(facet)
        {
          if (facet.facetfieldid !== 'commercecategory')
          {
            facets_to_include.push(facet.facetfieldid);
            // If the facet has an urlcomponent defined, then add it to the possible values list.
            facet.urlcomponent && facets_to_include.push(facet.urlcomponent);
    
            // Finally, include URL Component Aliases...
            _.each(facet.urlcomponentaliases, function(facet_alias)
            {
              facets_to_include.push(facet_alias.urlcomponent);
            });
          }
        });
    
        facets_to_include = _.union(facets_to_include, _.pluck(Configuration.get('facets'), 'id'));
        facets_to_include = _.uniq(facets_to_include);
        
        this.facetsToInclude = facets_to_include;
        
        this.isCategoryPage = !!category;
    
        if (_.isBoolean(category) && category)
        {
          var index = facets.length
          ,  facetsToInclude = this.facetsToInclude.slice(0);
    
          facetsToInclude.push(this.configuration.facetDelimiters.betweenFacetsAndOptions);
    
          _.each(facetsToInclude, function(facetname)
          {
            var i = facets.indexOf(facetname);
    
            if (i !== -1 && i < index)
            {
              index = i;
            }
          });
    
          var categoryUrl = facets.substring(0, index);
    
          facets = facets.substring(index);
    
          if (categoryUrl[0] !== '/')
          {
            categoryUrl = '/' + categoryUrl;
          }
    
          if (categoryUrl[categoryUrl.length - 1] === '/')
          {
            categoryUrl = categoryUrl.substring(0, categoryUrl.length - 1);
          }
    
          this.categoryUrl = categoryUrl;
        }
    
        else if (_.isString(category))
        {
          this.categoryUrl = category;
        }
        
        // We cast on top of the passed in parameters.
        if (facets && options)
        {
          this.facets = facets;
          this.options = options;
        }
    
        else if (_.isString(facets))
        {
          // It's a url
          this.parseUrl(facets);
        }
    
        else if (facets)
        {
          // It's an optional API object
          this.parseOptions(facets);
        }
      } 
    
                  
  6. Save the file.

Step 2. Prepare the Developer Tools For Your Override

  1. To make sure that the Gulp tasks include your modules when you deploy, create the ns.package.json file for the custom module and modify the distro.json file.

    Open the Modules/extensions/FacetsExtension@1.0.0 module directory.

  2. Create a file in this directory and name it ns.package.json.

    Modules/extensions/FacetsExtension@1.0.0/ns.package.json

  3. Build the ns.package.json file using the following code (where X.Y.Z indicates the current version of your module in SuiteCommerce):

                    {
       "gulp": {
          "javascript": [
             "JavaScript/*.js"
          ]
       },
          "overrides": {
          "suitecommerce/Facets@X.Y.Z/JavaScript/Facets.Translations.js" : 
          "JavaScript/Facets.Translations.js"
        }   
       }
    } 
    
                  
  4. Save the ns.package.json file.

  5. Open the distro.json file.

    This file is located in the top-level directory of your SuiteCommerce Advanced source code.

  6. In the distro.json file, add your custom modules to the modules object.

    Your code should look similar to the following example:

                        {
        "name": "SuiteCommerce Advanced Elbrus",
        "version": "2.0",
        "buildToolsVersion": "1.3.0",
        "folders": {
            "modules": "Modules",
            "suitecommerceModules": "Modules/suitecommerce",
            "extensionsModules": "Modules/extensions",
            "thirdPartyModules": "Modules/third_parties",
            "distribution": "LocalDistribution",
            "deploy": "DeployDistribution"
        },
        "modules": {
            "extensions/FacetsExtension": "1.0.0",
            ... 
    
                  

    This ensures that the Gulp tasks include your module when you deploy. In this example, the custom modules are added at the beginning of the list of modules. However, you can add the modules anywhere in the modules object. The order of precedence in this list does not matter.

  7. Save the distro.json file.

Step 3: Test and Deploy Your Extension

  1. Test your source code customizations on a local server (see Test SCA Customizations on a Local Server) or deploy them to your NetSuite account (see Deploy SCA Customizations to NetSuite). If you are currently running SCA on a local server, your changes should appear on your local site immediately.

  2. Confirm your results.

    Upon successful deployment, the URL used to access products by category includes the proper delimiters and shows the products instead of a 404 error.

Related Topics

SCA Patches

General Notices