@vtex/vtexis-compatibility-layer
Version:
Compatibility layer between intelligent search and VTEX
376 lines (375 loc) • 18.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.convertSearchDocument = exports.itemsFromSearchDocuments = void 0;
const utils_1 = require("./utils");
const searchableClustersFromDocument = (ProductClusterNames, ProductClusterSearchableIds) => {
const searchableClusters = {};
ProductClusterSearchableIds.forEach((cluster) => {
searchableClusters[cluster.toString()] = ProductClusterNames[cluster.toString()];
});
return searchableClusters;
};
const categoriesFromDocument = (CategoriesFullPath, CategoriesName, translations) => {
const categories = [];
CategoriesFullPath.forEach((path) => {
let categoryPath = '/';
const ids = path.split('/').filter((value) => value);
ids.forEach((id) => {
var _a;
const translatedName = (_a = utils_1.getTranslationInfo('CategoryName', translations, id)) !== null && _a !== void 0 ? _a : CategoriesName[id];
categoryPath = categoryPath.concat(`${translatedName}/`);
});
categories.push(categoryPath);
});
return categories;
};
/**
* Convert SKU Document Images to SearchImage format,
* keeping the main image at the beginning of the array
* */
const convertDocumentImages = (images, account) => {
const searchImages = [];
images.forEach((image) => {
const ticks = utils_1.dateToTicks(image.LastModified);
const img = {
imageId: image.ImageId,
cacheId: image.ImageId,
imageLabel: image.ImageLabel,
imageText: image.ImageText,
imageTag: image.ImageTag,
imageUrl: image.ImagePath.replace('~', `https://${account}.vtexassets.com`)
.replace(/(-#width#)|(-#height#)/g, '')
.concat(`?v=${ticks}`),
};
image.IsMain ? searchImages.unshift(img) : searchImages.push(img);
});
return searchImages;
};
const specificationsInfoFromDocument = (SpecificationGroups, translations) => {
const specificationsInfo = {};
const specificationGroups = [];
const properties = [];
SpecificationGroups.forEach((group) => {
const groupSpecs = group.Specifications.filter((specification) => specification.SpecificationValues.filter((spec) => spec.Value).length).map((specification) => ({
...specification,
SpecificationValues: specification.SpecificationValues.filter((spec) => spec.Value),
}));
if (!groupSpecs.length) {
return;
}
groupSpecs.forEach(({ Name, SpecificationValues }) => {
specificationsInfo[Name] = SpecificationValues.map((value) => value.Value);
});
groupSpecs
.filter((specification) => { var _a; return !((_a = specification.Field) === null || _a === void 0 ? void 0 : _a.IsStockKeppingUnit); })
.map((specification) => {
var _a;
return ({
name: (_a = utils_1.getTranslationInfo('SpecificationName', translations, specification.FieldId)) !== null && _a !== void 0 ? _a : specification.Name,
originalName: specification.Name,
values: specification.SpecificationValues.map((spec) => { var _a; return (_a = utils_1.getTranslationInfo('SpecificationValue', translations, spec.Id)) !== null && _a !== void 0 ? _a : spec.Value; }),
});
})
.forEach((spec) => properties.push(spec));
const visibleSpecs = groupSpecs.filter((spec) => { var _a; return spec.IsOnProductDetails && !((_a = spec.Field) === null || _a === void 0 ? void 0 : _a.IsStockKeppingUnit); });
if (visibleSpecs.length) {
specificationGroups.push({
name: group.GroupName,
originalName: group.GroupName,
specifications: visibleSpecs.map((specification) => {
var _a;
return ({
name: (_a = utils_1.getTranslationInfo('SpecificationName', translations, specification.FieldId)) !== null && _a !== void 0 ? _a : specification.Name,
originalName: specification.Name,
values: specification.SpecificationValues.map((spec) => { var _a; return (_a = utils_1.getTranslationInfo('SpecificationValue', translations, spec.Id)) !== null && _a !== void 0 ? _a : spec.Value; }),
});
}),
});
}
specificationsInfo[group.GroupName] = groupSpecs.map((spec) => spec.Name);
});
const allSpecifications = specificationGroups.flatMap((specGroup) => specGroup.specifications);
specificationGroups.push({
name: 'allSpecifications',
originalName: 'allSpecifications',
specifications: allSpecifications,
});
return {
...specificationsInfo,
specificationGroups,
properties,
};
};
const skuSpecificationsFromDocuments = (allSpecGroups, translations) => {
const skuSpecs = [];
const groupedSpecs = {};
allSpecGroups.flat().forEach((specGroup) => {
const skuSpecsFromGroup = specGroup.Specifications.filter((spec) => { var _a; return (_a = spec.Field) === null || _a === void 0 ? void 0 : _a.IsStockKeppingUnit; });
if (skuSpecsFromGroup.length) {
skuSpecs.push(...skuSpecsFromGroup);
}
});
skuSpecs.forEach((specification) => {
var _a, _b;
if (!specification.SpecificationValues.length) {
return;
}
const currentSpecValues = (_b = (_a = groupedSpecs[specification.FieldId]) === null || _a === void 0 ? void 0 : _a.SpecificationValues) !== null && _b !== void 0 ? _b : [];
const newSpecValues = specification.SpecificationValues.filter((specValue) => {
return currentSpecValues.findIndex((item) => item.Id === specValue.Id) === -1;
});
groupedSpecs[specification.FieldId] = {
Name: specification.Name,
SpecificationValues: currentSpecValues.concat(newSpecValues),
};
});
return Object.keys(groupedSpecs).map((key) => {
var _a;
const item = groupedSpecs[key];
return {
field: {
name: (_a = utils_1.getTranslationInfo('SpecificationName', translations, key)) !== null && _a !== void 0 ? _a : item.Name,
originalName: item.Name,
},
values: item.SpecificationValues.map((value) => {
var _a;
return ({
name: (_a = utils_1.getTranslationInfo('SpecificationValue', translations, value.Id)) !== null && _a !== void 0 ? _a : value.Value,
originalName: value.Value,
});
}),
};
});
};
const getSpotPrice = (paymentOptions, priceWithoutDiscount) => {
const installments = paymentOptions === null || paymentOptions === void 0 ? void 0 : paymentOptions.InstallmentOptions.flatMap((option) => option.Installments);
const installment = installments === null || installments === void 0 ? void 0 : installments.find((inst) => inst.Count && inst.ValueAsInt && inst.ValueAsInt / 100 < priceWithoutDiscount);
return installment ? installment.ValueAsInt / 100 : priceWithoutDiscount;
};
const buildInstallments = (paymentOptions, unitMultiplier) => {
const installments = [];
paymentOptions === null || paymentOptions === void 0 ? void 0 : paymentOptions.InstallmentOptions.forEach((option) => {
option.Installments.forEach((installment) => {
installments.push({
NumberOfInstallments: installment.Count,
Name: '',
Value: installment.ValueAsInt / 100 / unitMultiplier,
InterestRate: installment.InterestRateAsInt,
PaymentSystemName: option.PaymentName,
PaymentSystemGroupName: option.PaymentGroupName,
TotalValuePlusInterestRate: (installment.ValueAsInt / 100 / unitMultiplier) * installment.Count,
});
});
});
return installments;
};
const buildTeasers = (rnbData) => {
return rnbData.Teaser.map((teaser) => {
const conditions = {
parameters: teaser.TeaserCondition.ConditionParameters.map((parameter) => ({
name: parameter.Name,
value: parameter.Value,
})),
minimumQuantity: teaser.TeaserCondition.MinimumQuantity,
};
const effects = {
parameters: teaser.TeaserEffect.EffectParameters.map((parameter) => ({
name: parameter.Name,
value: parameter.Value,
})),
};
// check return when values are undefined
return {
name: teaser.Name,
featured: teaser.Featured,
id: teaser.Id,
teaserType: teaser.TeaserType,
conditions,
effects,
};
});
};
const buildCommercialOffer = (skuDocumentOffer, unitMultiplier) => {
var _a, _b;
return {
Price: skuDocumentOffer.Price,
ListPrice: skuDocumentOffer.ListPrice,
PriceWithoutDiscount: skuDocumentOffer.PriceWithoutDiscount,
Tax: (_a = skuDocumentOffer.Tax) !== null && _a !== void 0 ? _a : 0,
taxPercentage: ((_b = skuDocumentOffer.Tax) !== null && _b !== void 0 ? _b : 0) / skuDocumentOffer.Price,
RewardValue: skuDocumentOffer.RewardValue,
PriceValidUntil: skuDocumentOffer.PriceValidUntil,
CacheVersionUsedToCallCheckout: skuDocumentOffer.CacheVersionUsedToCallCheckout,
spotPrice: getSpotPrice(skuDocumentOffer.PaymentOptions, skuDocumentOffer.PriceWithoutDiscount),
AvailableQuantity: skuDocumentOffer.AvailableQuantity,
teasers: skuDocumentOffer.RatesAndBenefitsData ? buildTeasers(skuDocumentOffer.RatesAndBenefitsData) : [],
Installments: buildInstallments(skuDocumentOffer.PaymentOptions, unitMultiplier),
GiftSkuIds: [],
BuyTogether: [],
DeliverySlaSamples: skuDocumentOffer.AvailableQuantity
? [
{
DeliverySlaPerTypes: [],
Region: null,
},
]
: [],
ItemMetadataAttachment: [],
};
};
/**
* Define which seller should be the default, which should be the
* seller that has availability and the lowest price
* */
const setDefaultSeller = (sellers) => {
const availableSellers = sellers.filter((seller) => seller.active && seller.commertialOffer.AvailableQuantity > 0 && seller.commertialOffer.PriceWithoutDiscount > 0);
const [defaultSeller] = availableSellers.sort((a, b) => (a.commertialOffer.Price > b.commertialOffer.Price ? 1 : -1));
const sellersWithDefault = sellers.map((seller) => {
return {
sellerId: seller.sellerId,
sellerDefault: defaultSeller ? seller.sellerId === defaultSeller.sellerId : seller.sellerId === '1',
sellerName: seller.sellerName,
addToCartLink: seller.addToCartLink,
commertialOffer: seller.commertialOffer,
};
});
return sellersWithDefault.sort((sellerA) => (sellerA.sellerDefault ? -1 : 1));
};
const getSkuSellers = (offer, unitMultiplier) => {
const [salesChannel] = Object.keys(offer.SkuCommercialOfferPerSalesChannel);
const seller1 = offer.Seller1
? {
active: true,
sellerId: offer.Seller1.SellerId,
sellerName: offer.Seller1.Name,
sellerDefault: false,
addToCartLink: '',
commertialOffer: buildCommercialOffer(offer.SkuCommercialOfferPerSalesChannel[salesChannel], unitMultiplier),
}
: null;
const otherSellers = offer.SkuSellers.filter((seller) => seller.AvailableSalesChannels.includes(Number(salesChannel))).map((seller) => {
return {
active: seller.IsActive,
sellerId: seller.SellerId,
sellerName: seller.SellerName,
sellerDefault: false,
addToCartLink: '',
commertialOffer: buildCommercialOffer(seller.SkuCommercialOffer, unitMultiplier),
};
});
return setDefaultSeller((seller1 ? [seller1] : []).concat(otherSellers));
};
const getSkuSubscriptions = (agregatedAttachments) => agregatedAttachments.filter((attachment) => attachment.Name.includes('vtex.subscription.') && attachment.IsActive);
const getSkuVariations = (specificationGroups, attachments, translations) => {
const attributes = [];
specificationGroups.forEach((group) => {
const filteredSpecs = group.Specifications.filter((specification) => { var _a; return specification.SpecificationValues.filter((spec) => spec.Value).length && ((_a = specification.Field) === null || _a === void 0 ? void 0 : _a.IsStockKeppingUnit); }).map((specification) => ({
...specification,
SpecificationValues: specification.SpecificationValues.filter((spec) => spec.Value),
}));
filteredSpecs.forEach((spec) => {
spec.SpecificationValues.forEach((value) => {
var _a, _b;
attributes.push({
key: (_a = utils_1.getTranslationInfo('SpecificationName', translations, spec.Field.Id.toString())) !== null && _a !== void 0 ? _a : spec.Field.Name,
value: (_b = utils_1.getTranslationInfo('SpecificationValue', translations, value.Id)) !== null && _b !== void 0 ? _b : value.Value,
});
});
});
});
const subscriptions = getSkuSubscriptions(attachments).map((subscription) => ({
key: 'activeSubscriptions',
value: subscription.Name.replace('vtex.subscription.', ''),
}));
const variations = attributes.concat(subscriptions).map((attr) => ({
name: attr.key,
values: [attr.value],
}));
return variations;
};
const getAttachments = (attachments) => {
return attachments.map((attachment) => ({
id: attachment.Id,
name: attachment.Name,
domainValues: attachment.DomainValues,
required: attachment.IsRequired,
}));
};
const itemsFromSearchDocuments = (documents, offers, account, translations) => {
const items = [];
documents.forEach((skuDocument) => {
var _a, _b, _c, _d;
const offer = offers[skuDocument.Id];
if (!offer) {
return;
}
const variations = getSkuVariations(skuDocument.SpecificationGroups, skuDocument.AgregatedAttachments, translations);
const images = convertDocumentImages(skuDocument.Images, account);
const attachments = getAttachments(skuDocument.AgregatedAttachments);
const nameComplete = `${(_a = utils_1.getTranslationInfo('ProductName', translations, skuDocument.ProductId)) !== null && _a !== void 0 ? _a : skuDocument.ProductName} ${(_b = utils_1.getTranslationInfo('SkuName', translations, skuDocument.Id)) !== null && _b !== void 0 ? _b : skuDocument.Name}`;
const searchItem = {
itemId: skuDocument.Id,
name: (_c = utils_1.getTranslationInfo('SkuName', translations, skuDocument.Id)) !== null && _c !== void 0 ? _c : skuDocument.Name,
nameComplete,
complementName: skuDocument.NameComplement,
ean: (_d = skuDocument.AlternateIds.Ean) !== null && _d !== void 0 ? _d : '',
referenceId: [
{
Key: 'RefId',
Value: skuDocument.AlternateIds.RefId,
},
],
measurementUnit: skuDocument.MeasurementUnit,
unitMultiplier: skuDocument.UnitMultiplier,
modalType: skuDocument.ModalType,
images,
Videos: skuDocument.Videos,
variations,
sellers: getSkuSellers(offer, skuDocument.UnitMultiplier),
attachments,
isKit: skuDocument.IsKit,
kitItems: [],
};
items.push(searchItem);
});
return items;
};
exports.itemsFromSearchDocuments = itemsFromSearchDocuments;
const convertSearchDocument = async (documents, offers, account, translations) => {
var _a, _b, _c, _d;
const [{ ProductId, ProductName, BrandId, BrandName, NameComplement, LinkId, ProductRefId, AlternateIds, DirectCategoryId, ProductTitle, MetaTagDescription, ReleaseDate, ProductClusterHighlights, ProductClusterNames, ProductClusterSearchableIds, CategoriesFullPath, CategoriesName, Description, SpecificationGroups, },] = documents;
const searchableClusters = searchableClustersFromDocument(ProductClusterNames, ProductClusterSearchableIds);
const categories = categoriesFromDocument(CategoriesFullPath, CategoriesName, translations);
const specificationsInfo = specificationsInfoFromDocument(SpecificationGroups, translations);
const skuSpecifications = skuSpecificationsFromDocuments(documents.map((document) => document.SpecificationGroups), translations);
const items = exports.itemsFromSearchDocuments(documents, offers, account, translations);
const product = {
categories,
categoriesIds: CategoriesFullPath,
productId: ProductId,
productName: (_a = utils_1.getTranslationInfo('ProductName', translations)) !== null && _a !== void 0 ? _a : ProductName,
cacheId: `sp-${ProductId}`,
productReference: ProductRefId || AlternateIds.RefId,
linkText: LinkId,
brand: (_b = utils_1.getTranslationInfo('BrandName', translations)) !== null && _b !== void 0 ? _b : BrandName,
brandId: BrandId,
link: `https://portal.vtexcommercestable.com.br/${LinkId}/p`,
description: (_c = utils_1.getTranslationInfo('Description', translations)) !== null && _c !== void 0 ? _c : Description,
items,
priceRange: utils_1.getPriceRange(items),
categoryId: DirectCategoryId.toString(),
productTitle: ProductTitle,
metaTagDescription: (_d = MetaTagDescription !== null && MetaTagDescription !== void 0 ? MetaTagDescription : NameComplement) !== null && _d !== void 0 ? _d : '',
clusterHighlights: utils_1.objToNameValue('id', 'name', ProductClusterHighlights),
productClusters: utils_1.objToNameValue('id', 'name', ProductClusterNames),
searchableClusters,
titleTag: '',
categoryTree: [],
skuSpecifications,
...specificationsInfo,
origin: 'search-document',
releaseDate: ReleaseDate,
};
return [product];
};
exports.convertSearchDocument = convertSearchDocument;