Created
September 11, 2013 17:51
-
-
Save jaredholdcroft/6527236 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.jag.middleware.core.mapper; | |
import java.math.BigDecimal; | |
import java.text.NumberFormat; | |
import java.text.SimpleDateFormat; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.Collections; | |
import java.util.Comparator; | |
import java.util.Date; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.SortedSet; | |
import java.util.TreeMap; | |
import java.util.TreeSet; | |
import javax.xml.bind.JAXBContext; | |
import javax.xml.bind.JAXBException; | |
import javax.xml.bind.Marshaller; | |
import org.w3c.dom.Document; | |
import org.apache.commons.collections.CollectionUtils; | |
import org.apache.commons.lang.StringUtils; | |
import com.demandware.schema.catalog.Catalog; | |
import com.demandware.schema.catalog.ComplexTypeCategory; | |
import com.demandware.schema.catalog.ComplexTypeCategoryAssignment; | |
import com.demandware.schema.catalog.ComplexTypeHeader; | |
import com.demandware.schema.catalog.ComplexTypeImageInternalLocation; | |
import com.demandware.schema.catalog.ComplexTypeImageSettings; | |
import com.demandware.schema.catalog.ComplexTypeImageViewTypes; | |
import com.demandware.schema.catalog.ComplexTypeProduct; | |
import com.demandware.schema.catalog.ComplexTypeProductImage; | |
import com.demandware.schema.catalog.ComplexTypeProductImageGroup; | |
import com.demandware.schema.catalog.ComplexTypeProductImages; | |
import com.demandware.schema.catalog.ComplexTypeProductVariations; | |
import com.demandware.schema.catalog.ComplexTypeProductVariationsAttributes; | |
import com.demandware.schema.catalog.ComplexTypeProductVariationsVariant; | |
import com.demandware.schema.catalog.ComplexTypeProductVariationsVariants; | |
import com.demandware.schema.catalog.ComplexTypeRecommendation; | |
import com.demandware.schema.catalog.ComplexTypeVariationAttribute; | |
import com.demandware.schema.catalog.ComplexTypeVariationAttributeValue; | |
import com.demandware.schema.catalog.ObjectFactory; | |
import com.demandware.schema.catalog.SharedTypeCustomAttribute; | |
import com.demandware.schema.catalog.SharedTypeLocalizedString; | |
import com.demandware.schema.catalog.SharedTypeLocalizedText; | |
import com.demandware.schema.catalog.SharedTypeSiteSpecificBoolean; | |
import com.demandware.schema.catalog.SharedTypeSiteSpecificCustomAttribute; | |
import com.demandware.schema.catalog.SharedTypeSiteSpecificDateTime; | |
import com.demandware.schema.catalog.SharedTypeSiteSpecificSiteMapChangeFrequency; | |
import com.demandware.schema.catalog.SharedTypeSiteSpecificSiteMapPriority; | |
import com.demandware.schema.catalog.SimpleTypeCategoryAssignmentPosition; | |
import com.demandware.schema.catalog.SimpleTypeImportMode; | |
import com.demandware.schema.catalog.SimpleTypeRecommendationSourceType; | |
import com.demandware.schema.catalog.SimpleTypeSiteMapChangeFrequency; | |
import com.jag.middleware.core.catalog.DeletedEntity; | |
import com.jag.middleware.core.category.AttributeConstants; | |
import com.jag.middleware.core.category.AttributeValue; | |
import com.jag.middleware.util.DateUtils; | |
import com.stormhq.commerce.catalog.CatalogExport; | |
import com.stormhq.commerce.catalog.asset.ProductCategoryColorMapping; | |
import com.stormhq.commerce.catalog.asset.DigitalAsset; | |
import com.stormhq.commerce.catalog.asset.DigitalAssetType; | |
import com.stormhq.commerce.catalog.category.Category; | |
import com.stormhq.commerce.catalog.product.BaseProduct; | |
import com.stormhq.commerce.catalog.product.Product; | |
import com.stormhq.commerce.catalog.product.ProductGroup; | |
import com.stormhq.commerce.catalog.product.ProductGrouping; | |
import com.stormhq.commerce.catalog.product.ProductSKU; | |
import com.stormhq.core.lookup.service.LookupDataService; | |
import java.util.Arrays; | |
public class CatalogMapper extends BaseJaxbMapper<CatalogExport, Catalog> { | |
/** | |
* Demandware node name for category child product assignment. | |
*/ | |
public static final String DW_NODE_CATEGORY_PG_ASSIGNMENT = "category-assignment"; | |
public static final String DW_NODE_IMAGE= "image"; | |
/** | |
* This isn't actually a DW node name. We use it as a delete type to signify | |
* and attribute which needs to be blanked out. | |
*/ | |
public static final String DW_NODE_PG_ATTR_VALUE = "pg-attr-value"; | |
public static final String DW_NODE_PRODUCT_RECOMMENDATION = "cross-sell"; | |
public static final String DW_NODE_PRODUCT = "product"; | |
/** | |
* String delimiter to separate attribute code value and its index. | |
*/ | |
public static final String ATTR_ALT_DISPLAY_DELIMITER = "_"; | |
public static final String ATTRIBUTE_ID_PRODUCT_TYPE_DELIMITER = "-"; | |
public static final String ATTR_ID_VARIANT_COLOR_CHIP = "variantColorChip"; | |
public static final String ATTR_ID_VARIANT_STYLE_CODE = "productStyleCode"; | |
/** | |
* Define defaultProdImage for a variant, so it overrides the master. | |
* Otherwise, we would have two sets of images. | |
*/ | |
private static final String ATTR_ID_VARIANT_PROD_IMAGE = "defaultProdImage"; //"variantProdImage"; | |
public static final String DEMANDWARE_ROOT = "root"; | |
private ObjectFactory objectFactory = new ObjectFactory(); | |
/** | |
* The root category ID. If we have a category that matches this, we should send to | |
* Demandware with an ID of "root". | |
*/ | |
private Integer rootCategoryId; | |
/** | |
* The catalog ID for Demandware. | |
* This has to match something in Business Manager, otherwise the file is ignored. | |
*/ | |
private String catalogId; | |
/** | |
* The brand. Not really used for anything, but Demandware has a field for it. | |
*/ | |
private String brand; | |
/** | |
* Map of JAG image codes to DW image types. | |
*/ | |
private Map<String,String> exportImageTypes = new TreeMap<String,String>(); | |
private AttributeComparatorFactory comparatorFactory; | |
private Map<String, Comparator<ProductSKU>> skuComparators; | |
/** | |
* Used to look up export attributes. | |
*/ | |
private LookupDataService lookupDataService; | |
@Override | |
protected void marshal(Object object,Document doc) throws JAXBException { | |
JAXBContext jaxbContext = JAXBContext.newInstance(getMarshalPackageName(object)); | |
Marshaller marshaller = jaxbContext.createMarshaller(); | |
//marshaller.setProperty(marshaller.JAXB_SCHEMA_LOCATION, ""); | |
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); | |
marshaller.setProperty(Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION, "catalog.xsd"); | |
marshaller.marshal(object,doc); | |
} | |
@Override | |
protected Catalog objectToObject(CatalogExport export) { | |
// This method can actually generate a single XML file | |
// for exporting the entire CatalogExport object, | |
// but in practice we have to break the export into parts | |
// so that they're exported in the right order. | |
// For example, the catalog-product assignments | |
// have to be sent after the category and the product are in place. | |
// init | |
initializeImageTypes(export.getAssetTypes()); | |
// Export should be pre-separated by brand. | |
Catalog catalog = objectFactory.createCatalog(); | |
catalog.setCatalogId(catalogId); | |
//Check if we want to export asset types via <header> element | |
if(export.isExportAssetTypes()) { | |
catalog.setHeader(assetTypeHeader(catalog, export.getAssetTypesByDWOrder())); | |
} | |
// Export categories | |
if (!export.getCategories().isEmpty()) { | |
for (Category c : export.getCategories().values()) { | |
if (c.isDeleted()) { | |
ComplexTypeCategory deletedCategory = objectFactory | |
.createComplexTypeCategory(); | |
deletedCategory.setCategoryId(c.getId().toString()); | |
deletedCategory.setMode(SimpleTypeImportMode.DELETE); | |
catalog.getCategory().add(deletedCategory); | |
} else if (!c.getId().equals(export.getRootCategoryId()) || | |
c.getId().equals(this.rootCategoryId)) { | |
// If this is a full publish (export.root == mapper.root), | |
// create a node for the parent category. | |
// Otherwise, just create nodes for the children. | |
ComplexTypeCategory mappedCategory = createCategory(c); | |
catalog.getCategory().add(mappedCategory); | |
} | |
} | |
} | |
// Export products | |
TreeMap<String, TreeMap<String, String>> sharedAttributeMap = new TreeMap<String, TreeMap<String, String>>(); | |
if (CollectionUtils.isNotEmpty(export.getProducts())) { | |
for (Product p : export.getProducts()) { | |
// variation master. | |
try { | |
catalog.getProduct().add(createVariationMaster(p, export.getAssetTypesByDWOrder())); | |
} catch (Exception e) { | |
warn("Exception while mapping product "+p.getProductGroup().toString()); | |
e.printStackTrace(); | |
} | |
// variants | |
for (ProductSKU sku : p.getProductGroup().getSkus()) { | |
// Delta SKU publish: suppressed for 1.7 until product save logic can be fixed. | |
if (sku.getLastPublishedDate()==null || sku.getModifiedDate().after(sku.getLastPublishedDate())) { | |
catalog.getProduct().add(createVariant(sku, p)); | |
} | |
// Check for customer-selectable attributes. | |
for (AttributeValue av : sku.getAttributeValues()) { | |
if (av.getAttribute().isCustomerSelectable()) { | |
// Store shared attributes in the master list. | |
String attributeName = av.getAttribute().getName(); | |
if (sharedAttributeMap.get(attributeName) == null) { | |
sharedAttributeMap.put(attributeName, | |
new TreeMap<String, String>()); | |
} | |
if (av.isDefault()) { | |
sharedAttributeMap.get(attributeName).put(av.getCode(), | |
av.getDisplayName()); | |
} | |
} | |
} | |
} | |
} | |
} | |
// Category-product mappings | |
// These are done separately. | |
// | |
if (export.getCategoryProductMappings().size() > 0) { | |
for (Integer cid : export.getCategoryProductMappings().keySet()) { | |
int idx=0; | |
for (Integer pid : export.getCategoryProductMappings().get(cid)) { | |
ComplexTypeCategoryAssignment assignment = categoryAssignment(cid, pid); | |
// add sort order. | |
assignment.setPosition(SimpleTypeCategoryAssignmentPosition.AUTO); | |
assignment.setCustomAttributes(objectFactory.createSharedTypeCustomAttributes()); | |
SharedTypeCustomAttribute attribute = objectFactory.createSharedTypeCustomAttribute(); | |
attribute.setAttributeId("sortOrder"); | |
attribute.getContent().add(String.valueOf(idx++)); | |
assignment.getCustomAttributes().getCustomAttribute().add(attribute); | |
if ( cid.equals(export.getProductDefaultCategory().get(pid)) ) { | |
assignment.setPrimaryFlag(Boolean.TRUE); | |
} | |
catalog.getCategoryAssignment().add(assignment); | |
} | |
} | |
// Add deletes later. | |
} | |
// Cross sells are done separately. | |
for (ProductGrouping grouping : export.getProductGroupings()) { | |
if (grouping.getGroupingType().equals(ProductGrouping.CROSS_SELL)) { | |
ComplexTypeRecommendation recommendation = createProductRecommendation(grouping); | |
catalog.getRecommendation().add(recommendation); | |
} | |
} | |
// Handle deletes. | |
// Product attribute deletes handled above with the product. | |
for (DeletedEntity entity : export.getDeletes()) { | |
if (entity.getEntityType().equals(DW_NODE_CATEGORY_PG_ASSIGNMENT)) { | |
ComplexTypeCategoryAssignment assignment = categoryAssignment(entity.getPrimaryId(),entity.getSecondaryId()); | |
assignment.setMode(SimpleTypeImportMode.DELETE); | |
catalog.getCategoryAssignment().add(assignment); | |
} else if (entity.getEntityType().equals(DW_NODE_PRODUCT_RECOMMENDATION)) { | |
ProductGrouping grouping = new ProductGrouping(); | |
grouping.setProductGroupId(entity.getPrimaryId()); | |
grouping.setRelatedGroupId(entity.getSecondaryId()); | |
ComplexTypeRecommendation recommendation = | |
createProductRecommendation(grouping); | |
recommendation.setMode(SimpleTypeImportMode.DELETE); | |
catalog.getRecommendation().add(recommendation); | |
} else if (entity.getEntityType().equals(DW_NODE_PRODUCT)) { | |
ComplexTypeProduct product = objectFactory.createComplexTypeProduct(); | |
product.setMode(SimpleTypeImportMode.DELETE); | |
if (entity.getPrimaryId() != null) { | |
// PG ID | |
product.setProductId(entity.getPrimaryId().toString()); | |
} else if (StringUtils.isNotBlank(entity.getStringId())) { | |
// SKU UPC | |
product.setProductId(entity.getStringId()); | |
} | |
catalog.getProduct().add(product); | |
} else if (entity.getEntityType().equals(DW_NODE_IMAGE)) { | |
// No action; these are deleted directly from the Impex. | |
} | |
} | |
debug("Creating shared attributes list"); | |
// Shared attribute map. | |
// Export shared attributes | |
// We need to show all values (i.e., not just the ones in this export)-- | |
// Abandoning this unless we have a good solution for this problem. | |
// (i.e., all variation attributes will have local scope) | |
// for (String attributeId : sharedAttributeMap.keySet()) { | |
// info("Shared customer-selectable attribute: " + attributeId); | |
// addVariationAttribute(catalog, attributeId, attributeId, | |
// sharedAttributeMap.get(attributeId)); | |
// } | |
return catalog; | |
} | |
/** | |
* Create the asset header | |
* @param catalog | |
* @param assetTypes | |
* @return | |
*/ | |
protected ComplexTypeHeader assetTypeHeader(Catalog catalog, List<DigitalAssetType> assetTypes) { | |
ComplexTypeHeader header = objectFactory.createComplexTypeHeader(); | |
ComplexTypeImageSettings imageSettings = objectFactory.createComplexTypeImageSettings(); | |
header.setImageSettings(imageSettings); | |
ComplexTypeImageInternalLocation internalLocation = objectFactory.createComplexTypeImageInternalLocation(); | |
internalLocation.setBasePath("/"); | |
imageSettings.setInternalLocation(internalLocation); | |
imageSettings.setVariationAttributeId("variantColor"); | |
imageSettings.setAltPattern(getBrand() + " Product"); | |
imageSettings.setTitlePattern(getBrand() + " Product"); | |
ComplexTypeImageViewTypes viewTypes = objectFactory.createComplexTypeImageViewTypes(); | |
imageSettings.setViewTypes(viewTypes); | |
if(CollectionUtils.isNotEmpty(assetTypes)) { | |
//Sort order | |
List<DigitalAssetType> sortList = AssetTypeComparator.sort(assetTypes); | |
for (DigitalAssetType digitalAssetType : sortList) { | |
String demandWareName = digitalAssetType.getDemandwareNameOrDefault(); | |
if (!viewTypes.getViewType().contains(demandWareName)) { | |
viewTypes.getViewType().add(demandWareName); | |
} | |
} | |
} | |
return header; | |
} | |
protected void initializeImageTypes(Collection<DigitalAssetType> assetTypes) { | |
exportImageTypes.clear(); | |
for (DigitalAssetType t : assetTypes) { | |
String dwName = t.getDemandWareName(); // TODO I think this should be t.getDemandwareNameOrDefault() | |
if (StringUtils.isNotBlank(dwName)) { | |
exportImageTypes.put(t.getStandard(), dwName); | |
if (t.hasEnlarged()) { | |
exportImageTypes.put(t.getEnlarged(), dwName); | |
} | |
if (t.hasZoomable()) { | |
exportImageTypes.put(t.getZoomable(), dwName); | |
} | |
} | |
} | |
} | |
/** | |
* @param cid | |
* @param pid | |
* @return | |
*/ | |
private ComplexTypeCategoryAssignment categoryAssignment(Integer cid, Integer pid) { | |
ComplexTypeCategoryAssignment assignment = objectFactory | |
.createComplexTypeCategoryAssignment(); | |
assignment.setCategoryId(cid.toString()); | |
assignment.setProductId(pid.toString()); | |
return assignment; | |
} | |
/** | |
* Create a <code>category</code> tag. | |
* | |
* @param c the category object to be mapped. | |
* @return | |
*/ | |
protected ComplexTypeCategory createCategory(Category c) { | |
ComplexTypeCategory mappedCategory = objectFactory | |
.createComplexTypeCategory(); | |
mappedCategory.setCategoryId(getCategoryId(c.getId())); | |
mappedCategory.getDisplayName().add( | |
createSharedTypeLocalizedString(c.getDisplayName())); | |
mappedCategory.setOnlineFlag(Boolean.valueOf(c.isLiveOnSite())); | |
mappedCategory.setTemplate(""); | |
mappedCategory.setSitemapIncludedFlag(Boolean.valueOf(c.isVisible())); | |
mappedCategory.setSitemapChangefrequency(SimpleTypeSiteMapChangeFrequency.DAILY); | |
mappedCategory.setSitemapPriority(Double.valueOf(1.0)); | |
if(c.getOnlineFrom()!= null) | |
mappedCategory.setOnlineFrom(DateUtils.xmlDate(c.getOnlineFrom())); | |
if(c.getOnlineTo()!= null) | |
mappedCategory.setOnlineTo(DateUtils.xmlDate(c.getOnlineTo())); | |
if (c.getParent() != null) { | |
// this is a non-root category node. | |
// Parent Id | |
mappedCategory.setParent(getCategoryId(c.getParent().getId())); | |
// sort order | |
mappedCategory.setPosition(Double.valueOf(c.getPosition())); | |
// Custom Attributes | |
mappedCategory.setCustomAttributes(objectFactory | |
.createSharedTypeCustomAttributes()); | |
// old sort order | |
// mappedCategory.getCustomAttributes().getCustomAttribute().add( | |
// createSharedTypeCustomAttribute("sortOrder",String.valueOf(sortOrderIndex))); | |
// Only use description if this is not the root. | |
mappedCategory.getDescription().add( | |
createSharedTypeLocalizedString(c.getDescription())); | |
} | |
return mappedCategory; | |
} | |
/** | |
* Return the category ID. Generally, this is the same ID as from the | |
* database, but there is one special case: the root category must have an | |
* ID of "root", per Demandware's specification. | |
* | |
* @param catId | |
* @return the category ID. | |
*/ | |
private String getCategoryId(Integer catId) { | |
// Is the parent the root? Then the category ID is "root" per DW spec. | |
if (catId.equals(rootCategoryId)) | |
return DEMANDWARE_ROOT; | |
return catId.toString(); | |
} | |
/** | |
* @param p | |
* @return product object containing variation master node. | |
* @throws Exception | |
*/ | |
protected ComplexTypeProduct createVariationMaster(Product p, Collection<DigitalAssetType> assetTypes) throws Exception { | |
ComplexTypeProduct mappedProduct = objectFactory.createComplexTypeProduct(); | |
mappedProduct.setEan(""); | |
mappedProduct.setUpc(""); | |
mappedProduct.setUnit(""); | |
ProductGroup pg = p.getProductGroup(); | |
mappedProduct.setProductId(pg.getId().toString()); | |
mappedProduct.getDisplayName().add( | |
createSharedTypeLocalizedString(pg.getDisplayName())); | |
mappedProduct.getShortDescription().add( | |
createSharedTypeLocalizedText(pg.getShortDescription())); | |
mappedProduct.getLongDescription().add( | |
createSharedTypeLocalizedText(pg.getLongDescription())); | |
// Don't use standard image fields; use custom attributes instead. | |
// mappedProduct.setImage(p.getProductGroupImageName("PD")); | |
// mappedProduct.setThumbnail(p.getProductGroupImageName("VA")); | |
mappedProduct.setMinOrderQuantity(new BigDecimal(1)); | |
mappedProduct.setStepQuantity(new BigDecimal(1)); | |
mappedProduct.setAvailableFlag(Boolean.valueOf(pg.isLiveOnSite())); | |
mappedProduct.getOnlineFlag().add(siteSpecificBoolean(pg.isLiveOnSite())); | |
mappedProduct.getSearchableFlag().add(siteSpecificBoolean( | |
pg.isLiveOnSite() && pg.isSearchable())); | |
//Add images here | |
createImages(mappedProduct, p, assetTypes); | |
//Set schedule dates | |
setScheduleDates(pg, mappedProduct); | |
mappedProduct.setTaxClassId("Standard"); | |
// | |
AttributeValue brandAv = pg.getAttributeValueByAttributeName(AttributeConstants.VARIANT_BRAND); | |
if (brandAv != null) { | |
mappedProduct.setBrand(brandAv.getDisplayName()); | |
} else { | |
mappedProduct.setBrand(this.getBrand()); | |
} | |
mappedProduct.setManufacturerName(this.getBrand()); | |
mappedProduct.getSitemapIncludedFlag().add(siteSpecificBoolean(pg.isVisible())); | |
SharedTypeSiteSpecificSiteMapChangeFrequency freq = objectFactory.createSharedTypeSiteSpecificSiteMapChangeFrequency(); | |
//freq.setSiteId(catalogId); | |
freq.setValue(SimpleTypeSiteMapChangeFrequency.DAILY); | |
mappedProduct.getSitemapChangefrequency().add(freq); | |
SharedTypeSiteSpecificSiteMapPriority siteMapPriority = objectFactory.createSharedTypeSiteSpecificSiteMapPriority(); | |
//siteMapPriority.setSiteId(catalogId); | |
siteMapPriority.setValue(1.0); | |
mappedProduct.getSitemapPriority().add(siteMapPriority); | |
////////////////////////////////////////////// | |
// Custom attributes: | |
////////////////////////////////////////////// | |
// pg attributes | |
SortedSet<AttributeValue> customAttrs = new TreeSet<AttributeValue>(); | |
customAttrs.addAll(pg.getAttributeValues()); | |
customAttrs.addAll(p.getDeletedAttributes()); | |
for (AttributeValue av : customAttrs) { | |
// Use display names. Attributes are not normalized for product groups. | |
// Some of these might be deleted attribute values, which are to be sent as blank. | |
addCustomAttributeToProduct(mappedProduct, | |
av.getAttribute().getName(), av.getDisplayName()); | |
} | |
////////////////////////////////////////////// | |
// Non-attribute custom attributes: | |
// These custom attributes don't come from the attributes table. | |
////////////////////////////////////////////// | |
// Add custom attribute for product style code (code name). | |
if (CollectionUtils.isEmpty(p.getIncludedStyles())) { | |
addCustomAttributeToProduct(mappedProduct, ATTR_ID_VARIANT_STYLE_CODE, "Style Code: "+pg.getCodeName()); | |
} else { | |
List<String> styleCodes = new ArrayList<String>(); | |
styleCodes.add(pg.getCodeName()); | |
styleCodes.addAll(p.getIncludedStyles()); | |
addCustomAttributeToProduct(mappedProduct, ATTR_ID_VARIANT_STYLE_CODE, "Style Codes: "+StringUtils.join(styleCodes, ", ")); | |
} | |
// Add custom attribute for group code name and number | |
addCustomAttributeToProduct(mappedProduct, "coordinateGroupName", pg.getCoordinateGroupDescription()); | |
addCustomAttributeToProduct(mappedProduct, "coordinateGroupCode", pg.getCoordinateGroupCode()); | |
// Product type | |
addCustomAttributeToProduct(mappedProduct, "productType", pg.getProductType()); | |
// "Also available in" | |
List<String> alsoAvailable = new ArrayList<String>(); | |
AttributeValue petiteStyle = pg.getAttributeValueByAttributeName(AttributeConstants.PETITE_STYLE); | |
if (petiteStyle != null) { | |
alsoAvailable.add("Petite"); | |
} | |
AttributeValue plusStyle = pg.getAttributeValueByAttributeName(AttributeConstants.PLUS_STYLE); | |
if (plusStyle != null) { | |
alsoAvailable.add("Plus"); | |
} | |
addCustomAttributeToProduct(mappedProduct, "alsoAvailable", alsoAvailable); | |
// color mappings for each category | |
// and default color code | |
List<String> productCategoryImages = new ArrayList<String>(); | |
for (ProductCategoryColorMapping color : p.getDefaultColorAssignments()) { | |
// default color code. | |
String colorCode = ""; | |
for (AttributeValue colorAttr : pg.getSkuColors()) { | |
if (color.getColor().equals(colorAttr.getCode())) { | |
colorCode = getNormalizedAttrValue(colorAttr); | |
} | |
} | |
Collection<DigitalAsset> images = p.getImages().get(color.getColor()); | |
if (rootCategoryId.equals(color.getCategoryId())) { | |
// pg images | |
addCustomAttributeToProduct(mappedProduct, "defaultProdImage", createImageAttributeValue(images)); | |
// default color code. | |
for (AttributeValue colorAttr : pg.getSkuColors()) { | |
if (color.getColor().equals(colorAttr.getCode())) { | |
addCustomAttributeToProduct(mappedProduct, "defaultProductColorCode", colorCode); | |
} | |
} | |
} else if (images != null && !images.isEmpty()) { | |
String path = viewAllImage(images); | |
if (StringUtils.isNotBlank(path)) { | |
StringBuilder imageString = new StringBuilder(); | |
imageString.append(color.getCategoryId()); | |
imageString.append("|"); | |
imageString.append(colorCode); | |
imageString.append("|"); | |
imageString.append(path); | |
productCategoryImages.add(imageString.toString()); | |
} else { | |
warn("No view-all for color ["+color.getColor()+"]"); | |
} | |
} else { | |
warn("No images for color ["+color.getColor()+"]"); | |
} | |
} | |
mappedProduct.getCustomAttributes().getCustomAttribute().add( | |
createSharedTypeCustomAttribute("defaultProductCategoryViewAllImage", productCategoryImages)); | |
// generate "search colors" attribute | |
mappedProduct.getCustomAttributes().getCustomAttribute().add( | |
searchColorCustomAttribute(p, "searchColors", false)); | |
// pg prices | |
NumberFormat numberInstance = NumberFormat.getNumberInstance(); | |
numberInstance.setMinimumFractionDigits(2); | |
numberInstance.setMaximumFractionDigits(2); | |
numberInstance.setGroupingUsed(false); | |
addCustomAttributeToProduct(mappedProduct, "listPrice", numberInstance.format(pg.getListPrice())); | |
addCustomAttributeToProduct(mappedProduct, "maxSellPrice", numberInstance.format(pg.getMaxSellPrice())); | |
addCustomAttributeToProduct(mappedProduct, "minSellPrice", numberInstance.format(pg.getSellPrice())); | |
addCustomAttributeToProduct(mappedProduct, "onSale", pg.isOnSale() ? "true":"false"); | |
mappedProduct.getSearchableIfUnavailableFlag().add(siteSpecificBoolean(pg.isSoldOutAllowed())); | |
// Deprecated: use searchable-if-unavailable | |
addCustomAttributeToProduct(mappedProduct, "allowSoldOut", pg.isSoldOutAllowed() ? "true":"false"); | |
// 4. Shoprunner | |
addCustomAttributeToProduct(mappedProduct, "sr_eligible", pg.isShoprunnerEligible()?"Yes":"No"); | |
////////////////////////////////////////////// | |
// Create Variants list | |
////////////////////////////////////////////// | |
// SKU attributes and SKUs | |
Collection<ProductSKU> skus = pg.getSkus(); | |
info(pg.toString()+": making variants list of "+skus.size()+" SKUs"); | |
ComplexTypeProductVariations variations = objectFactory | |
.createComplexTypeProductVariations(); | |
// Variants: link to SKUs | |
variations.setVariants(createVariantList(skus, pg.getProductType())); | |
// Variation Attribute definitions | |
// These are the attributes and attribute values that are shared across all variants for this style. | |
if (skus.size() > 0) { | |
ComplexTypeProductVariationsAttributes variationAttributes = | |
objectFactory.createComplexTypeProductVariationsAttributes(); | |
// Customer-selectable only. | |
List<String> variationAttributeNames = | |
getVariationAttributeNames( pg.getProductType() ); | |
// Iterate through attribute values for all SKUs. | |
for (String attributeId : variationAttributeNames ) | |
{ | |
ComplexTypeVariationAttribute variationAttribute = createVariationAttribute(skus, attributeId,pg.getProductType()); | |
if (variationAttribute != null) { | |
variationAttributes.getAttributeOrVariationAttributeOrSharedVariationAttribute().add(variationAttribute); | |
} | |
} | |
variations.setAttributes(variationAttributes); | |
} | |
// Done with variants, add to product. | |
mappedProduct.setVariations(variations); | |
return mappedProduct; | |
} | |
protected String viewAllImage(Collection<DigitalAsset> images) { | |
for (DigitalAsset asset : images) { | |
if (asset.isViewAll()) | |
return asset.getResourcePath(); | |
} | |
return ""; | |
} | |
private SharedTypeSiteSpecificCustomAttribute searchColorCustomAttribute(Product p, String attributeName, boolean useAll) | |
throws Exception | |
{ | |
Map<String, String> searchColors = new TreeMap<String,String>(); | |
for (AttributeValue color : p.getProductGroup().getSkuColors()) { | |
if (useAll || p.getProductGroup().getLiveOnSiteByColor(color.getCode()) > 0) { | |
String colorCode = getNormalizedAttrValue(color); | |
String generalColorValue = ""; | |
for (AttributeValue generalColor : p.getProductGroup().getSkuGeneralColors()) { | |
if (generalColor.getCode().equals(color.getCode())) { | |
generalColorValue = generalColor.getDisplayName(); | |
} | |
} | |
StringBuilder row = new StringBuilder(); | |
row.append(colorCode); | |
row.append("|"); | |
row.append(color.getDisplayName()); | |
row.append("|"); | |
row.append(generalColorValue); | |
row.append("|"); | |
Collection<DigitalAsset> images = p.getImages().get(color.getCode()); | |
if (images != null && !images.isEmpty()) { | |
row.append(viewAllImage(images)); | |
} else { | |
warn("No images for color ["+colorCode+"]"); | |
} | |
row.append("|"); | |
row.append(getColorChip(images)); | |
searchColors.put(colorCode, row.toString()); | |
} | |
} | |
SharedTypeSiteSpecificCustomAttribute searchColorCustomAttribute = | |
createSharedTypeCustomAttribute(attributeName, | |
new ArrayList<String>(searchColors.values())); | |
return searchColorCustomAttribute; | |
} | |
/** | |
* Creates image node having all the images by asset type. This should be called | |
* at the master level | |
* @param mappingProduct ComplexTypeProduct, product node | |
* @param product Product group | |
* @param assetTypes Asset types | |
*/ | |
protected void createImages(ComplexTypeProduct mappingProduct, Product product, Collection<DigitalAssetType> assetTypes) { | |
if(assetTypes.isEmpty()) | |
return; | |
ComplexTypeProductImages images = objectFactory.createComplexTypeProductImages(); | |
mappingProduct.setImages(images); | |
// For variant master images (i.e default images), | |
// leave color blank | |
for (ProductCategoryColorMapping colorMappings : product.getDefaultColorAssignments()) { | |
if (rootCategoryId.equals(colorMappings.getCategoryId())) { | |
createImageGroups(images, null, product.getImages().get(colorMappings.getColor()), assetTypes); | |
break; | |
} | |
} | |
//Group all the assets by color | |
for (AttributeValue color : product.getProductGroup().getSkuColors()) { | |
// variation value has to match attribute value | |
// If the attribute value is normalized, then so too must the image group. | |
String normalizedColor = getNormalizedAttrValue(color); | |
if(product.getImages().containsKey(color.getCode())) { | |
createImageGroups(images, normalizedColor, product.getImages().get(color.getCode()), assetTypes); | |
} | |
} | |
cleanUpImages(images); | |
} | |
/** | |
* This method removes all the images group, which does not have any images | |
* e.g | |
<image-group variation-value="WHITELE" view-type="Left"> | |
<image path="null" /> | |
<image path="null" /> | |
<image path="null" /> | |
</image-group> | |
* On the other hand, this is valid e.g as there is one image available | |
* <image-group variation-value="WHITELE" view-type="Main"> | |
<image path="product/PG.NWACCOLIA.WHITELE.PD.jpg" /> | |
<image path="null" /> | |
<image path="null" /> | |
</image-group> | |
* As is this: | |
* <image-group variation-value="WHITELE" view-type="Main"> | |
<image path="null" /> | |
<image path="null2" /> | |
<image path="product/PG.NWACCOLIA.WHITELE.PZ.jpg" /> | |
</image-group> | |
but notice that we call the second image null2. | |
* @param images | |
*/ | |
protected void cleanUpImages(ComplexTypeProductImages images) { | |
List<ComplexTypeProductImageGroup> removeProductImageGroup = new ArrayList<ComplexTypeProductImageGroup>(); | |
for (ComplexTypeProductImageGroup imageGroup : images.getImageGroup()) { | |
List<ComplexTypeProductImage> imageGroupImages = imageGroup.getImage(); | |
int nNull=0; | |
for (ComplexTypeProductImage image : imageGroupImages) { | |
// Does have a valid path, so break out. | |
if(image.getPath().equals("null")) { | |
nNull++; | |
if (nNull > 1) { | |
image.setPath("null"+nNull); | |
} | |
} else { | |
break; | |
} | |
} | |
if(imageGroupImages.size()==nNull) { | |
removeProductImageGroup.add(imageGroup); | |
} | |
} | |
images.getImageGroup().removeAll(removeProductImageGroup); | |
} | |
/** | |
* Generate image groups for a color (or the default color). | |
* For each asset type, check if it exists in the list of digital assets. | |
* If it exists, then export digital assets resource path, | |
* else export "null". | |
* <p> | |
<image-group view-type="BACK"> <-- for each asset type (view-type) | |
<image path="Test/PG.0191919NWR.GINGBMM.BK.jpg" /> <-- asset found | |
<image path="Test/PG.0191919NWR.GINGBMM.BE.jpg" /> <-- asset found | |
<image path="null" /> <-- asset not found | |
</image-group> | |
<image-group view-type="MAIN" variation-value="BLACKMM"> <-- variation value defined if color is not null | |
<image path="Test/PG.0191919NWR.GINGBMM.PD.jpg" /> <-- asset found | |
<image path="Test/PG.0191919NWR.GINGBMM.PE.jpg" /> <-- asset found | |
<image path="null" /> <-- asset not found | |
</image-group> | |
</p> | |
* @param images ComplexTypeProductImages | |
* @param color Digital asset color | |
* @param assets Product SKU assets (DigitalAsset) | |
* @param assetTypes Asset types (DigitalAssetType) | |
*/ | |
protected void createImageGroups(ComplexTypeProductImages images, | |
String color, Collection<DigitalAsset> assets, | |
Collection<DigitalAssetType> assetTypes) | |
{ | |
for (DigitalAssetType imageType : assetTypes) { | |
String demandWareName = imageType.getDemandwareNameOrDefault(); | |
ComplexTypeProductImageGroup imageGroup = createImageGroup(images, demandWareName, color); | |
// Find the image with standard image code (e.g "PD") | |
// If it exists, create an XML node, otherwise put path=null | |
// If the image type has enlarged and zoomable types, | |
// Add nodes for those (or placeholders with path=null) | |
DigitalAsset standardImage = findDigitalAsset(imageType.getStandard(), assets); | |
if (imageType.hasEnlarged() || imageType.hasZoomable()) { | |
// Find the image with enlarged image code ("PE") | |
DigitalAsset enlargedImage = findDigitalAsset(imageType.getEnlarged(), assets); | |
// Find the image with enlarged image code ("PZ") | |
DigitalAsset zoomableImage = findDigitalAsset(imageType.getZoomable(), assets); | |
if (zoomableImage != null) { | |
// Just send zoomable | |
imageGroup.getImage().add(createImageNode(zoomableImage, "null")); | |
} else { | |
imageGroup.getImage().add(createImageNode(standardImage, "null")); | |
imageGroup.getImage().add(createImageNode(enlargedImage, "null")); | |
imageGroup.getImage().add(createImageNode(zoomableImage, "null")); | |
} | |
} else if (standardImage != null) { | |
imageGroup.getImage().add(createImageNode(standardImage, "null")); | |
} | |
} | |
} | |
/** | |
* Get the existing image group, or create one if not found. | |
* | |
* Creates ProductImageGroup. If ProductImageGroup has been already created for a given | |
* DemandWareName then do not create a new one. As long as following conditions are met. | |
* The Idea is to send all images under same image group | |
* a) If ProductImageGroup already exists for a given DemandWareName and the its | |
* "variation-value" (color) is null and the color passed to this method is null. | |
* In such situation return the existing ProductImageGroup (If color is null then its default color). | |
* b) If ProductImageGroup already exists for a given DemandWareName and the its | |
* "variation-value" (color) is not null and the color passed to this method | |
* matches with variation-value. In such situation return the existing ProductImageGroup. | |
* Otherwise create a new ProductImageGroup. | |
* @param images ComplexTypeProductImages has many ComplexTypeProductImageGroup | |
* @param demandwareViewName Demandware name for Digital asset type | |
* @param color Digital asset type color | |
* @return | |
*/ | |
protected ComplexTypeProductImageGroup createImageGroup( | |
ComplexTypeProductImages images, String demandwareViewName, String color) | |
{ | |
// See if we already have an image group | |
for (ComplexTypeProductImageGroup imageGroup : images.getImageGroup()) { | |
if(StringUtils.isNotBlank(imageGroup.getViewType()) | |
&& imageGroup.getViewType().equals(demandwareViewName)) { | |
//Now check if the found ProductImageGroup's color (variation-value) is null | |
//and the passed color is null | |
if(imageGroup.getVariationValue() == null && color == null) | |
return imageGroup; | |
//Other wise check if the found ProductImageGroup's color matches with the passed | |
//color | |
else if(imageGroup.getVariationValue() != null | |
&& imageGroup.getVariationValue().equals(color)) | |
return imageGroup; | |
} | |
} | |
//Looks like either the ProductImageGroup is not found or the above two conditions | |
// are not met, so create a new ProductImageGroup | |
ComplexTypeProductImageGroup newImageGroup = objectFactory.createComplexTypeProductImageGroup(); | |
newImageGroup.setViewType(demandwareViewName); | |
// variation-value is never defined for default color assets | |
if (StringUtils.isNotBlank(color)) { | |
newImageGroup.setVariationValue(color); | |
} | |
images.getImageGroup().add(newImageGroup); | |
return newImageGroup; | |
} | |
/** | |
* @param imageGroup | |
* @param digitalAsset | |
* @param defaultName the path name to use if the asset is null. | |
*/ | |
private ComplexTypeProductImage createImageNode(DigitalAsset digitalAsset, String defaultName) { | |
ComplexTypeProductImage image = objectFactory.createComplexTypeProductImage(); | |
if(digitalAsset == null) { | |
image.setPath(defaultName); | |
} else { | |
image.setPath(digitalAsset.getResourcePath()); | |
} | |
return image; | |
} | |
/** | |
* Find an asset matching an image type | |
* @param imageType | |
* @param assets | |
* @return the matching asset, or NULL if there is no match. | |
*/ | |
private DigitalAsset findDigitalAsset(String imageType, Collection<DigitalAsset> assets) { | |
if (assets != null) { | |
for (DigitalAsset asset : assets) { | |
if(asset.getImageType().equals(imageType)) | |
return asset; | |
} | |
} | |
return null; | |
} | |
/** | |
* Concatenates all of the image info into one string. | |
* | |
* image_attr_value := image_def ( |image_attr_value ) image_def := | |
* IMAGE_SIZE|IMAGE_TYPE|IMAGE_FILE_NAME | |
* | |
* @param assets | |
* collection of images. | |
* @return a list of encoded strings for each image. | |
* @deprecated This attribute may go away now that we also send DW image-group nodes. | |
*/ | |
@Deprecated | |
protected List<String> createImageAttributeValue(Collection<DigitalAsset> assets) { | |
List<String> images = new ArrayList<String>(); | |
if (assets != null) { | |
for (DigitalAsset i : assets) { | |
// Skip color chip | |
String imageType = i.getImageType(); | |
if (imageType.equals(DigitalAsset.IMAGE_TYPE_COLOR_CHIP)|| | |
imageType.equals(DigitalAsset.IMAGE_TYPE_GENERIC_COLOR_CHIP)) { | |
continue; | |
} | |
StringBuilder imageSpec = new StringBuilder(); | |
// DW Specification refers to this as image size, | |
// but the method is getImageType(), | |
// not to be confused with image type below. | |
imageSpec.append(imageType).append( | |
"|"); | |
// image type: Main, Detail, Top, Bottom, etc. | |
String dwImageType = "Main"; | |
if (exportImageTypes.containsKey(imageType)) { | |
dwImageType = exportImageTypes.get(imageType); | |
} | |
imageSpec.append(dwImageType+"|"); | |
// Strip the path. | |
imageSpec.append(i.getResourcePath()); | |
images.add(imageSpec.toString()); | |
} | |
Collections.sort(images, new ImageLabelComparator()); | |
} | |
return images; | |
} | |
/** | |
* Get attribute value comparator by product type. | |
* If none exists, try to get the default comparator for the attribute. | |
* @param name the attribute name | |
* @param productType the product type | |
* @return the attribute comparator (that sorts by code value, not display value) | |
*/ | |
protected Comparator<String> getComparator(String name, String productType) { | |
if (comparatorFactory == null) return null; | |
return comparatorFactory.getComparator(name, productType); | |
} | |
private SharedTypeSiteSpecificDateTime siteSpecificDateTime(Date date) { | |
SharedTypeSiteSpecificDateTime ssDate = objectFactory.createSharedTypeSiteSpecificDateTime(); | |
// ssDate.setSiteId(catalogId); | |
ssDate.setValue(DateUtils.xmlDate(date)); | |
return ssDate; | |
} | |
protected void setScheduleDates(BaseProduct baseProduct, ComplexTypeProduct complexTypeProduct) { | |
Date scheduleStartDate = baseProduct.getScheduleStartDate(); | |
Date scheduleEndDate = baseProduct.getScheduleEndDate(); | |
if(scheduleStartDate != null) { | |
complexTypeProduct.getOnlineFrom().add(siteSpecificDateTime(scheduleStartDate)); | |
} else { | |
complexTypeProduct.getOnlineFrom().add(null); | |
} | |
if(scheduleEndDate != null) { | |
complexTypeProduct.getOnlineTo().add(siteSpecificDateTime(scheduleEndDate)); | |
} else { | |
complexTypeProduct.getOnlineTo().add(null); | |
} | |
} | |
/** | |
* @param productType the product type | |
* @return | |
*/ | |
private List<String> getVariationAttributeNames(String productType) { | |
List<String> variationAttributeNames = null; | |
// Check for product-type-specific attributes. | |
if (StringUtils.isNotBlank(productType)) { | |
variationAttributeNames = lookupDataService.findValues("catalog.mapper.product.type." + productType); | |
} | |
// Use default if no product-type-specific attributes exist. | |
if (CollectionUtils.isEmpty(variationAttributeNames)) { | |
variationAttributeNames = lookupDataService.findValues("catalog.mapper.product.type.default"); | |
} | |
//variant attribute names will be unsorted coming from the lookupdataservice | |
Collections.sort(variationAttributeNames); | |
return variationAttributeNames; | |
} | |
protected ComplexTypeVariationAttribute createVariationAttribute( | |
Collection<ProductSKU> skus, String attributeId, String productType) { | |
ComplexTypeVariationAttribute variationAttribute = objectFactory.createComplexTypeVariationAttribute(); | |
variationAttribute.setAttributeId(attributeId); | |
variationAttribute.setVariationAttributeId(attributeId); | |
variationAttribute.setVariationAttributeValues(objectFactory.createComplexTypeVariationAttributeValues()); | |
// TODO (JH): refactor to allow for non-code based sorting. For now variantColor is different... | |
if (attributeId.equalsIgnoreCase(AttributeConstants.COLOR_FABRIC)) { | |
List<AttributeValue> attributeValues = new ArrayList<>(); | |
for (ProductSKU sku : skus) { | |
AttributeValue attrValue = sku.getAttributeValueByAttributeName(attributeId); | |
attributeValues.add(attrValue); | |
} | |
if (attributeValues.isEmpty()) | |
return null; | |
else { | |
Collections.sort(attributeValues, AttributeValue.ListOrderComparator); | |
for (AttributeValue av : attributeValues) { | |
ComplexTypeVariationAttributeValue customAttrValue = objectFactory.createComplexTypeVariationAttributeValue(); | |
customAttrValue.setValue(av.getCode()); | |
// use hardwired "x-default" for now. | |
customAttrValue.getDisplayValue().add(createSharedTypeLocalizedString(av.getDisplayName(),"x-default")); | |
variationAttribute.getVariationAttributeValues().getVariationAttributeValue().add(customAttrValue); | |
} | |
return variationAttribute; | |
} | |
} else { | |
// Get all (distinct) values for each SKU | |
// Use a tree map; this will sort the attributes by natural order by default. | |
Map<String,String> attrValues = new TreeMap<>(); | |
// Use comparator if one is specified for this attribute. | |
Comparator<String> comp = getComparator(attributeId,productType); | |
if (comp != null){ | |
attrValues = new TreeMap<>(comp); | |
} | |
for (ProductSKU sku : skus) { | |
AttributeValue attrValue = sku.getAttributeValueByAttributeName(attributeId); | |
if (attrValue != null && attrValue.getAttribute().isCustomerSelectable()) { | |
// This is not default, append "-1" to the code. This will separate | |
// up to two sets of display values (one default, one non-default), but | |
// one of these days, we'll have more than two display values, | |
// at which point we'll either have to have a numeric priority/preference | |
// index, or we can order the attribute code-display value pairs in the db | |
// instead of in the context. | |
String key = getNormalizedAttrValue(attrValue); | |
attrValues.put(key, attrValue.getDisplayName()); | |
} else if(attributeId.equals(AttributeConstants.INVENTORY_EXPECTED_DATE) | |
&& sku.getInventory().getExpectedDateValue() != null) { | |
String value = getStringDate(sku.getInventory().getExpectedDateValue()); | |
attrValues.put(value, value); | |
} | |
} | |
if (attrValues.isEmpty()) | |
return null; | |
else { | |
for (String code : attrValues.keySet()) { | |
ComplexTypeVariationAttributeValue customAttrValue = objectFactory.createComplexTypeVariationAttributeValue(); | |
customAttrValue.setValue(code); | |
// use hardwired "x-default" for now. | |
customAttrValue.getDisplayValue().add(createSharedTypeLocalizedString(attrValues.get(code),"x-default")); | |
variationAttribute.getVariationAttributeValues().getVariationAttributeValue().add(customAttrValue); | |
} | |
return variationAttribute; | |
} | |
} | |
} | |
/** | |
* Creates the list of variants (skus) from the provided collection of skus | |
* @param skus | |
* @param productType the product type, used to find a comparator for ordering the SKUS. | |
* @return | |
*/ | |
protected ComplexTypeProductVariationsVariants createVariantList( | |
Collection<ProductSKU> skus, String productType) { | |
ComplexTypeProductVariationsVariants variants = objectFactory | |
.createComplexTypeProductVariationsVariants(); | |
List<ProductSKU> skuList = new ArrayList<ProductSKU>(skus); | |
// Check for comparator | |
if ( productType != null && skuComparators != null) { | |
debug("Looking for comparator for productType='" + productType + "'"); | |
Comparator<ProductSKU> comp = skuComparators.get(productType); | |
if ( comp != null ) { | |
Collections.sort(skuList, comp); | |
} | |
} | |
for (ProductSKU sku : skuList) { | |
ComplexTypeProductVariationsVariant v = objectFactory | |
.createComplexTypeProductVariationsVariant(); | |
//v1.setProductId(sku.getId().toString()); | |
v.setProductId(sku.getUpc()); | |
// we don't define default here, so no call to setDefault() | |
variants.getVariant().add(v); | |
} | |
return variants; | |
} | |
/** | |
* @param sku | |
* @return product object containing variant node. | |
*/ | |
private ComplexTypeProduct createVariant(ProductSKU sku, Product p) { | |
ComplexTypeProduct mappedProduct = objectFactory.createComplexTypeProduct(); | |
mappedProduct.setEan(""); | |
mappedProduct.setUpc(sku.getUpc()); | |
mappedProduct.setUnit(""); | |
mappedProduct.setProductId(sku.getUpc()); | |
mappedProduct.getDisplayName().add( | |
createSharedTypeLocalizedString(sku.getDisplayName())); | |
// Commented out: don't use standard image fields--use custom attribute | |
// instead. | |
// mappedProduct.setImage(p.getProductSKUImageName(sku, "PD")); | |
// mappedProduct.setThumbnail(p.getProductSKUImageName(sku, "VA")); | |
mappedProduct.setMinOrderQuantity(new BigDecimal(1)); | |
mappedProduct.setStepQuantity(new BigDecimal(1)); | |
// Sellability: TRUE if SKU is live-on-site AND parent PG is live-on-site | |
boolean sellable = p.getProductGroup().isLiveOnSite() && p.getProductGroup().isVisible() && sku.isLiveOnSite() && sku.isVisible(); | |
mappedProduct.setAvailableFlag(Boolean.valueOf(sellable)); | |
mappedProduct.getOnlineFlag().add(siteSpecificBoolean(sellable)); | |
mappedProduct.getSearchableFlag().add(siteSpecificBoolean(sellable && p.getProductGroup().isSearchable())); | |
//Set schedule dates | |
setScheduleDates(sku, mappedProduct); | |
mappedProduct.setTaxClassId("Standard"); | |
mappedProduct.setBrand(getBrand()); | |
mappedProduct.setManufacturerName(getBrand()); | |
mappedProduct.setManufacturerSku(""); | |
mappedProduct.setPageAttributes(objectFactory.createComplexTypePageAttributes()); | |
debug("createVariant(): " + sku.getAttributeValues().size() + " attribute values"); | |
// Variant attributes and attribute values | |
List<String> variationAttributeNames = getVariationAttributeNames(p.getProductGroup().getProductType()); | |
for (String attributeId : variationAttributeNames) { | |
AttributeValue av = sku.getAttributeValueByAttributeName(attributeId); | |
if (av != null) { | |
debug("Adding priority attribute: " + attributeId ); | |
addAttributeValueToProduct(mappedProduct, av); | |
} | |
} | |
// Non-db attributes | |
// 1. inventory expected date | |
if(sku.getInventory().getExpectedDateValue() != null) { | |
addCustomAttributeToProduct(mappedProduct | |
, AttributeConstants.INVENTORY_EXPECTED_DATE | |
, getStringDate(sku.getInventory().getExpectedDateValue())); | |
} | |
// 2. SKU color chip | |
Collection<DigitalAsset> images = p.getImages().get(sku.getColorFabricAttributeCode()); | |
addCustomAttributeToProduct(mappedProduct, ATTR_ID_VARIANT_COLOR_CHIP, getColorChip(images)); | |
// 3. product images: taken from digital asset table. | |
addCustomAttributeToProduct(mappedProduct, ATTR_ID_VARIANT_PROD_IMAGE, | |
createImageAttributeValue(images)); | |
// 4. Shoprunner | |
addCustomAttributeToProduct(mappedProduct, "sr_eligible", | |
sku.isShoprunnerEligible()&&p.getProductGroup().isShoprunnerEligible()?"Yes":"No"); | |
return mappedProduct; | |
} | |
/** | |
* Get a color chip for a list of assets. | |
* @param assets collection of assets | |
* @return empty string if not found | |
*/ | |
private String getColorChip(Collection<DigitalAsset> assets) { | |
if (assets != null && !assets.isEmpty()) { | |
for (DigitalAsset chip : assets) { | |
if (DigitalAsset.IMAGE_TYPE_COLOR_CHIP.equals(chip.getImageType()) | |
|| DigitalAsset.IMAGE_TYPE_GENERIC_COLOR_CHIP.equals(chip.getImageType())) | |
return chip.getResourcePath(); | |
} | |
} | |
return ""; | |
} | |
private void addAttributeValueToProduct(ComplexTypeProduct mappedProduct, | |
AttributeValue av) { | |
debug("Adding attribute: " + av.getDisplayName() + " (" | |
+ av.getCode() + ")"); | |
if (av.getAttribute().isCustomerSelectable()) { | |
// Shared attribute; normalize. | |
addCustomAttributeToProduct(mappedProduct, av.getAttribute().getName(), | |
getNormalizedAttrValue(av)); | |
} else { | |
addCustomAttributeToProduct(mappedProduct, av.getAttribute().getName(), | |
av.getDisplayName()); | |
} | |
} | |
/** | |
* Add a custom attribute with a single attribute value to a Product. | |
* @see CatalogMapper#addCustomAttributeToProduct(ComplexTypeProduct, String, List) | |
* @param mappedProduct | |
* @param attributeId attribute name | |
* @param attributeValue attribute value -- this gets wrapped into a list. | |
*/ | |
private void addCustomAttributeToProduct(ComplexTypeProduct mappedProduct, | |
String attributeId, String attributeValue) | |
{ | |
addCustomAttributeToProduct(mappedProduct, attributeId, Arrays.asList(attributeValue)); | |
} | |
/** | |
* Add a custom attribute to a Product. | |
* @param mappedProduct | |
* @param attributeId attribute name | |
* @param values list of attribute values | |
*/ | |
private void addCustomAttributeToProduct(ComplexTypeProduct mappedProduct, | |
String attributeId, List<String> values) { | |
if (mappedProduct.getCustomAttributes() == null) { | |
mappedProduct.setCustomAttributes(objectFactory | |
.createSharedTypeSiteSpecificCustomAttributes()); | |
} | |
mappedProduct.getCustomAttributes().getCustomAttribute().add( | |
createSharedTypeCustomAttribute(attributeId, values)); | |
} | |
private ComplexTypeRecommendation createProductRecommendation( | |
ProductGrouping grouping) { | |
ComplexTypeRecommendation recommendation = objectFactory.createComplexTypeRecommendation(); | |
// 1 - cross-sell | |
// 2 - up-sell | |
// 3 - other | |
recommendation.setType("1"); | |
recommendation.setSourceType(SimpleTypeRecommendationSourceType.PRODUCT); | |
recommendation.setSourceId(grouping.getProductGroupId().toString()); | |
recommendation.setTargetId(grouping.getRelatedGroupId().toString()); | |
return recommendation; | |
} | |
/** | |
* @param attributeId | |
* name of attribute | |
* @param content | |
* list of attribute values | |
* @param lang | |
* language | |
*/ | |
private SharedTypeSiteSpecificCustomAttribute createSharedTypeCustomAttribute( | |
String attributeId, List<String> content, String lang) { | |
SharedTypeSiteSpecificCustomAttribute attribute = objectFactory | |
.createSharedTypeSiteSpecificCustomAttribute(); | |
attribute.setAttributeId(attributeId); | |
attribute.setLang(lang); | |
// attribute.setSiteId(catalogId); | |
if (content.size() > 1) { | |
for (String s : content) { | |
debug("attribute: [" + attributeId + "], value=[" | |
+ s.toString() + "]"); | |
attribute.getContent().add( | |
objectFactory.createSharedTypeCustomAttributeValue(s)); | |
} | |
} else if (content.size() > 0){ | |
// Single value: Don't wrap in <value> tags | |
attribute.getContent().add(content.get(0)); | |
} | |
return attribute; | |
} | |
/** | |
* Equivalent to createSharedTypeCustomAttribute(attributeId, content, | |
* "x-default"); | |
* | |
* @param attributeId | |
* name of attribute | |
* @param content | |
* attribute value | |
*/ | |
private SharedTypeSiteSpecificCustomAttribute createSharedTypeCustomAttribute( | |
String attributeId, List<String> content) { | |
return createSharedTypeCustomAttribute(attributeId, content, null); | |
} | |
/** | |
* Return a DW localized text. | |
* | |
* @param value | |
* @return | |
*/ | |
private SharedTypeLocalizedText createSharedTypeLocalizedText( | |
String value, String lang) { | |
SharedTypeLocalizedText s = objectFactory.createSharedTypeLocalizedText(); | |
s.setValue(value); | |
if (lang != null) { | |
s.setLang(lang); | |
} | |
// s.setLang(lang == null?"x-default":lang); | |
return s; | |
} | |
/** | |
* Return a DW localized text, using "x-default" as the | |
* language. | |
* | |
* @param value | |
* @return | |
*/ | |
private SharedTypeLocalizedText createSharedTypeLocalizedText(String value) { | |
return createSharedTypeLocalizedText(value, null); | |
} | |
/** | |
* Return a DW localized string. | |
* | |
* @param value | |
* @return | |
*/ | |
private SharedTypeLocalizedString createSharedTypeLocalizedString( | |
String value, String lang) { | |
SharedTypeLocalizedString s = objectFactory | |
.createSharedTypeLocalizedString(); | |
s.setValue(value); | |
if (lang != null) { | |
s.setLang(lang); | |
} | |
// s.setLang(lang == null?"x-default":lang); | |
return s; | |
} | |
/** | |
* Return a DW localized string, using "x-default" as the | |
* language. | |
* | |
* @param value | |
* @return | |
*/ | |
private SharedTypeLocalizedString createSharedTypeLocalizedString(String value) { | |
return createSharedTypeLocalizedString(value, null); | |
} | |
private SharedTypeSiteSpecificBoolean siteSpecificBoolean(boolean value) { | |
SharedTypeSiteSpecificBoolean flag = objectFactory.createSharedTypeSiteSpecificBoolean(); | |
//flag.setSiteId(catalogId); | |
flag.setValue(value); | |
return flag; | |
} | |
/** | |
* @param attrValue | |
* @return | |
*/ | |
private String getNormalizedAttrValue(AttributeValue attrValue) { | |
return attrValue.getCode()+ (attrValue.isDefault()?"":ATTR_ALT_DISPLAY_DELIMITER+"1"); | |
} | |
private String getStringDate(Date date) { | |
SimpleDateFormat format = new SimpleDateFormat("MM/dd/yyyy"); | |
return format.format(date); | |
} | |
public Integer getRootCategoryId() { | |
return rootCategoryId; | |
} | |
public void setRootCategoryId(Integer rootCategoryId) { | |
this.rootCategoryId = rootCategoryId; | |
} | |
public String getCatalogId() { | |
return catalogId; | |
} | |
public void setCatalogId(String catalogId) { | |
this.catalogId = catalogId; | |
} | |
public String getBrand() { | |
return brand; | |
} | |
public void setBrand(String brand) { | |
this.brand = brand; | |
} | |
public Map<String, String> getExportImageTypes() { | |
return exportImageTypes; | |
} | |
public void setExportImageTypes(Map<String, String> imageCodeMappings) { | |
this.exportImageTypes = imageCodeMappings; | |
} | |
public AttributeComparatorFactory getComparatorFactory() { | |
return comparatorFactory; | |
} | |
public void setComparatorFactory(AttributeComparatorFactory comparatorFactory) { | |
this.comparatorFactory = comparatorFactory; | |
} | |
public void setSkuComparators( | |
Map<String, Comparator<ProductSKU>> skuComparators) { | |
this.skuComparators = skuComparators; | |
} | |
public Map<String, Comparator<ProductSKU>> getSkuComparators() { | |
return skuComparators; | |
} | |
public void setLookupDataService(LookupDataService lookupDataService) { | |
this.lookupDataService = lookupDataService; | |
} | |
public LookupDataService getLookupDataService() { | |
return lookupDataService; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment