UNPKG

@vtex/vtexis-compatibility-layer

Version:

Compatibility layer between intelligent search and VTEX

376 lines (375 loc) 18.6 kB
"use strict"; 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;