To obtain the custom fields, we must modify the model file for a quick and optimal solution. We need to mention the custom field ids for fetching, and the section below with the Enhancement heading is where the update is made; please check and understand the concept before working. Because custom fields do not return the API for Commerce Categories, we must combine them with the default ones.
/*
© 2020 NetSuite Inc.
User may not copy, modify, distribute, or re-bundle or otherwise make available this code;
provided, however, if you are an authorized user with a NetSuite account or log-in, you
may use this code subject to the terms that govern your access and use.
*/
// Category.js
// -----------
// Handles the Category tree
define('Categories.Model', [
'SC.Model',
'Application',
'SC.Models.Init',
'Configuration',
'underscore',
'SiteSettings.Model',
'Utils'
], function(SCModel, Application, ModelsInit, Configuration, _, SiteSettings, Utils) {
return SCModel.extend({
name: 'Category',
categoryColumns: {
boolean: ['displayinsite', 'isinactive', 'isprimaryurl'],
sideMenu: {
sortBy: 'sequencenumber',
fields: ['name', 'internalid', 'sequencenumber', 'urlfragment', 'displayinsite']
},
subCategories: {
sortBy: 'sequencenumber',
fields: [
'name',
'description',
'internalid',
'sequencenumber',
'urlfragment',
'thumbnailurl',
'displayinsite'
]
},
category: {
fields: [
'internalid',
'name',
'description',
'pagetitle',
'pageheading',
'pagebannerurl',
'addtohead',
'metakeywords',
'metadescription',
'displayinsite',
'urlfragment',
'idpath', // needed for breadcrumb
'fullurl', // needed for canonical path
'isprimaryurl' // needed for canonical path
]
},
// Enhancement #
// Summary: Custom fields does not return the API for the Commerce Categories
customFields: {
fields: [
'custrecord_bottom_description',
'custrecord_vv_fullpathname',
'custrecord_anchor_text',
'custrecord_vv_block_id',
'custrecord_vv_cat_id'
]
},
breadcrumb: {
fields: ['internalid', 'name', 'displayinsite']
},
menu: {
sortBy: 'sequencenumber',
fields: ['internalid', 'name', 'sequencenumber', 'displayinsite']
},
mapping: {
internalid: 'subcatid',
name: 'subcatnameoverride',
description: 'subcatdescoverride',
pagetitle: 'subcatpagetitleoverride',
pageheading: 'subcatpageheadingoverride',
pagebannerurl: 'subcatpagebannerurloverride',
addtohead: 'subcataddtoheadoverride',
metakeywords: 'subcatmetakeywordsoverride',
metadescription: 'subcatmetadescoverride',
displayinsite: 'subcatdisplayinsiteoverride',
sequencenumber: 'subcatsequencenumber',
thumbnailurl: 'subcatthumbnailurloverride',
urlfragment: 'subcaturlfragmentoverride'
}
},
isPersonalizedCatalogViewsEnabled: SiteSettings.isPersonalizedCatalogViewsEnabled(),
getColumns: function(element) {
const config = Configuration.get().categories;
return _.union(
this.categoryColumns[element].fields,
config[element].fields || config[element].additionalFields
).join();
},
getSortBy: function(element) {
const config = Configuration.get().categories;
return config[element].sortBy || this.categoryColumns[element].sortBy;
},
getNavigationItemOptionalFields: function() {
return (
'&bread_crumb_fields=' +
this.getColumns('breadcrumb') +
'&category_fields=' +
this.getColumns('category') +
'&side_menu_fields=' +
this.getColumns('sideMenu') +
'&subcategory_fields=' +
this.getColumns('subCategories')
);
},
getCategoryTreeOptionalFields: function() {
const config = Configuration.get().categories;
return '&menu_fields=' + this.getColumns('menu');
},
getSMTEndpointParameters: function(
field,
value,
as_of_date,
optional_fields,
pcv_groups,
pcv_all_items
) {
const Environment = Application.getEnvironment(request);
const locale = (
(Environment && Environment.currentLanguage).locale ||
ModelsInit.session.getShopperLanguageLocale()
).split('_');
let parameters =
'currency=' +
ModelsInit.session.getShopperCurrency().code +
'&site_id=' +
ModelsInit.session.getSiteSettings(['siteid']).siteid;
const use_pcv = this.isPersonalizedCatalogViewsEnabled ? 'T' : 'F';
parameters +=
'&c=' +
nlapiGetContext().getCompany() +
'&exclude_empty=' +
Configuration.get('categories.excludeEmptyCategories') +
'&use_pcv=' +
use_pcv +
'&pcv_all_items=' +
pcv_all_items +
'&language=' +
locale[0] +
'&country=' +
(locale[1] || '') +
'&' +
field +
'=' +
value +
optional_fields;
// Only in case of SMT call
if (as_of_date) {
parameters += '&as_of_date=' + as_of_date;
}
if (pcv_groups) {
parameters += '&pcv_groups=' + pcv_groups;
}
return parameters;
},
get: function(fullurl, effectiveDate, pcv_groups, pcv_all_items, runAsAdmin) {
const NAVIGATION_ITEM_ENDPOINT = '/api/navigation/v1/categorynavitems?';
let category = {};
const baseUrl = runAsAdmin
? request.getURL().match(/(^https?:\/\/[^\/]+)/i)[0]
: Utils.trim(Configuration.get().cms.baseUrl || '') ||
'http://' + Utils.getShoppingDomain();
const params = this.getSMTEndpointParameters(
'full_url',
fullurl,
effectiveDate,
this.getNavigationItemOptionalFields(),
pcv_groups,
pcv_all_items
);
const endpointURL = baseUrl + NAVIGATION_ITEM_ENDPOINT + params;
const requestHeader = {
Accept: 'application/json',
Cookie: Utils.replaceNewLineByASpace(request.getHeader('cookie'))
};
const enpointResponse = nlapiRequestURL(endpointURL, null, requestHeader);
if (enpointResponse.getCode() === 200) {
const response = JSON.parse(enpointResponse.getBody()).data;
if (response) {
category = response[0];
} else {
throw notFoundError;
}
} else {
throw notFoundError;
}
// Enhancement # -
// Summary: Custom fields does not return the API for the Commerce Categories
var customFields = this.categoryColumns['customFields'].fields;
category = this.getCustomFields(customFields, category);
// added on top of the workaround provided;
// making it work for subcategories & breadcrumbs too
category.categories = this.getSubCategoriesCustomFields(customFields, category.categories);
category.breadcrumb = this.getSubCategoriesCustomFields(customFields, category.breadcrumb);
this.sortBy(category.siblings, this.getSortBy('sideMenu'));
this.sortBy(category.categories, this.getSortBy('subCategories'));
return category;
},
// added on top of the workaround provided;
getSubCategoriesCustomFields: function (fields, subCategories) {
const self = this;
var categories = [];
_.each(subCategories, (subCat) => { categories.push(self.getCustomFields(fields, subCat)); });
return categories;
},
getMenuCustomFields: function (fields, subCategories) {
const self = this;
var categories = [];
nlapiLogExecution('DEBUG', 'subCategories', JSON.stringify(subCategories));
try {
_.each(subCategories, (subCat) => {
var category = subCat
category = self.getCustomFields(fields, subCat);
nlapiLogExecution('DEBUG', 'getCustomFields', JSON.stringify(category));
if (category.categories.length>0) {
category.categories = self.getSubCategoriesCustomFields(fields, category.categories);
nlapiLogExecution('DEBUG', 'childcategory', JSON.stringify(category.categories));
// _.each(category.categories, function (element) {
// if (element.categories.length > 0) {
// element.categories=self.getSubCategoriesCustomFields(fields, element.categories);
// }
// });
}
categories.push(self.getCustomFields(fields, category));
});
nlapiLogExecution('DEBUG', 'categoriesfunc', JSON.stringify(categories));
} catch (error) {
nlapiLogExecution('ERROR', 'newerror', error);
}
return categories;
},
// Enhancement #
// Summary: Custom fields does not return the API for the Commerce Categories
getCustomFields: (fields, category) => {
var usage = nlapiGetContext().getRemainingUsage();
nlapiLogExecution('ERROR', 'usage', usage);
var newCategory = category;
var columns = [];
var filter = new nlobjSearchFilter('internalid', null, 'is', category.internalid);
_.each(fields, (field) => { columns.push(new nlobjSearchColumn(field)); });
var result = nlapiSearchRecord('commercecategory', null, filter, columns);
_.each(fields, (field) => { newCategory[field] = result[0].getValue(field); });
return newCategory;
},
getCategoryTree: function(
level,
effectiveDate,
pcv_groups,
pcv_all_items,
runAsAdmin,
is_annonymous
) {
const CATEGORY_TREE_ENDPOINT = '/api/navigation/v1/categorynavitems/tree?';
let categoryTree = [];
const baseUrl = runAsAdmin
? request.getURL().match(/(^https?:\/\/[^\/]+)/i)[0]
: Utils.trim(Configuration.get().cms.baseUrl || '') ||
'http://' + Utils.getShoppingDomain();
const params = this.getSMTEndpointParameters(
'max_level',
level,
effectiveDate,
this.getCategoryTreeOptionalFields(),
pcv_groups,
pcv_all_items
);
nlapiLogExecution("DEBUG", "params", params);
const endpointURL = baseUrl + CATEGORY_TREE_ENDPOINT + params;
const requestHeader = {
Accept: 'application/json'
};
if (!is_annonymous) {
requestHeader.Cookie = Utils.replaceNewLineByASpace(request.getHeader('cookie'));
}
const enpointResponse = nlapiRequestURL(endpointURL, null, requestHeader);
if (enpointResponse.getCode() === 200) {
categoryTree = JSON.parse(enpointResponse.getBody()).data;
} else {
return [];
}
try {
// Enhancement # -
// Summary: Custom fields does not return the API for the Commerce Categories
var category = categoryTree
var customFields = this.categoryColumns['customFields'].fields;
category = this.getMenuCustomFields(customFields, category);
// added on top of the workaround provided;
// category.categories = this.getSubCategoriesCustomFields(customFields, category.categories);
categoryTree = category
} catch (error) {
nlapiLogExecution("DEBUG", "error", error);
}
this.sortBy(categoryTree, this.getSortBy('menu'));
return categoryTree;
},
sortBy: function(categories, property) {
if (categories) {
const self = this;
property = property || 'sequencenumber';
_.each(categories, function(category) {
self.sortBy(category.categories, property);
});
categories.sort(function(a, b) {
// This works with Strings, Numbers, and Numbers as Strings. ie: ['a', 'b', 'c'] OR [1, 3, 20] OR ['1', '3', '20']
const numberA = Number(a[property]);
const numberB = Number(b[property]);
if (!isNaN(numberA) && !isNaN(numberB)) {
return numberA - numberB;
}
return (a[property] + '').localeCompare(
b[property] + '',
{},
{
numeric: true
}
);
});
}
}
});
});