Java tutorial
/* * [y] hybris Platform * * Copyright (c) 2017 SAP SE or an SAP affiliate company. All rights reserved. * * This software is the confidential and proprietary information of SAP * ("Confidential Information"). You shall not disclose such Confidential * Information and shall use it only in accordance with the terms of the * license agreement you entered into with SAP. */ package com.daimler.spm.storefront.controllers.pages; import de.hybris.platform.acceleratorfacades.csv.CsvFacade; import de.hybris.platform.acceleratorfacades.flow.impl.SessionOverrideCheckoutFlowFacade; import de.hybris.platform.acceleratorservices.controllers.page.PageType; import de.hybris.platform.acceleratorservices.enums.CheckoutFlowEnum; import de.hybris.platform.acceleratorservices.enums.CheckoutPciOptionEnum; import de.hybris.platform.acceleratorstorefrontcommons.annotations.RequireHardLogIn; import de.hybris.platform.acceleratorstorefrontcommons.breadcrumb.ResourceBreadcrumbBuilder; import de.hybris.platform.acceleratorstorefrontcommons.constants.WebConstants; import de.hybris.platform.acceleratorstorefrontcommons.controllers.pages.AbstractCartPageController; import de.hybris.platform.acceleratorstorefrontcommons.controllers.util.GlobalMessages; import de.hybris.platform.acceleratorstorefrontcommons.forms.SaveCartForm; import de.hybris.platform.acceleratorstorefrontcommons.forms.UpdateQuantityForm; import de.hybris.platform.acceleratorstorefrontcommons.forms.VoucherForm; import de.hybris.platform.acceleratorstorefrontcommons.forms.validation.SaveCartFormValidator; import de.hybris.platform.acceleratorstorefrontcommons.util.XSSFilterUtil; import de.hybris.platform.cms2.exceptions.CMSItemNotFoundException; import de.hybris.platform.commercefacades.order.SaveCartFacade; import de.hybris.platform.commercefacades.order.data.CartData; import de.hybris.platform.commercefacades.order.data.CartModificationData; import de.hybris.platform.commercefacades.order.data.CommerceSaveCartParameterData; import de.hybris.platform.commercefacades.order.data.CommerceSaveCartResultData; import de.hybris.platform.commercefacades.order.data.OrderEntryData; import de.hybris.platform.commercefacades.product.ProductFacade; import de.hybris.platform.commercefacades.product.ProductOption; import de.hybris.platform.commercefacades.product.data.ProductData; import de.hybris.platform.commercefacades.quote.data.QuoteData; import de.hybris.platform.commercefacades.voucher.VoucherFacade; import de.hybris.platform.commercefacades.voucher.exceptions.VoucherOperationException; import de.hybris.platform.commerceservices.order.CommerceCartModificationException; import de.hybris.platform.commerceservices.order.CommerceSaveCartException; import de.hybris.platform.core.enums.QuoteState; import de.hybris.platform.enumeration.EnumerationService; import de.hybris.platform.site.BaseSiteService; import de.hybris.platform.util.Config; import com.daimler.spm.storefront.controllers.ControllerConstants; import java.io.IOException; import java.io.StringWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.util.StreamUtils; import org.springframework.validation.BindingResult; import org.springframework.validation.ObjectError; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.mvc.support.RedirectAttributes; /** * Controller for cart page */ @Controller @RequestMapping(value = "/cart") public class CartPageController extends AbstractCartPageController { public static final String SHOW_CHECKOUT_STRATEGY_OPTIONS = "storefront.show.checkout.flows"; public static final String ERROR_MSG_TYPE = "errorMsg"; public static final String SUCCESSFUL_MODIFICATION_CODE = "success"; public static final String VOUCHER_FORM = "voucherForm"; public static final String SITE_QUOTES_ENABLED = "site.quotes.enabled."; private static final String REDIRECT_CART_URL = REDIRECT_PREFIX + "/cart"; private static final String REDIRECT_QUOTE_EDIT_URL = REDIRECT_PREFIX + "/quote/%s/edit/"; private static final String REDIRECT_QUOTE_VIEW_URL = REDIRECT_PREFIX + "/my-account/my-quotes/%s/"; private static final Logger LOG = Logger.getLogger(CartPageController.class); @Resource(name = "simpleBreadcrumbBuilder") private ResourceBreadcrumbBuilder resourceBreadcrumbBuilder; @Resource(name = "enumerationService") private EnumerationService enumerationService; @Resource(name = "productVariantFacade") private ProductFacade productFacade; @Resource(name = "saveCartFacade") private SaveCartFacade saveCartFacade; @Resource(name = "saveCartFormValidator") private SaveCartFormValidator saveCartFormValidator; @Resource(name = "csvFacade") private CsvFacade csvFacade; @Resource(name = "voucherFacade") private VoucherFacade voucherFacade; @Resource(name = "baseSiteService") private BaseSiteService baseSiteService; @ModelAttribute("showCheckoutStrategies") public boolean isCheckoutStrategyVisible() { return getSiteConfigService().getBoolean(SHOW_CHECKOUT_STRATEGY_OPTIONS, false); } @RequestMapping(method = RequestMethod.GET) public String showCart(final Model model) throws CMSItemNotFoundException { return prepareCartUrl(model); } protected String prepareCartUrl(final Model model) throws CMSItemNotFoundException { final Optional<String> quoteEditUrl = getQuoteUrl(); if (quoteEditUrl.isPresent()) { return quoteEditUrl.get(); } else { prepareDataForPage(model); return ControllerConstants.Views.Pages.Cart.CartPage; } } protected Optional<String> getQuoteUrl() { final QuoteData quoteData = getCartFacade().getSessionCart().getQuoteData(); return quoteData != null ? (QuoteState.BUYER_OFFER.equals(quoteData.getState()) ? Optional.of(String.format(REDIRECT_QUOTE_VIEW_URL, urlEncode(quoteData.getCode()))) : Optional.of(String.format(REDIRECT_QUOTE_EDIT_URL, urlEncode(quoteData.getCode())))) : Optional.empty(); } /** * Handle the '/cart/checkout' request url. This method checks to see if the cart is valid before allowing the * checkout to begin. Note that this method does not require the user to be authenticated and therefore allows us to * validate that the cart is valid without first forcing the user to login. The cart will be checked again once the * user has logged in. * * @return The page to redirect to */ @RequestMapping(value = "/checkout", method = RequestMethod.GET) @RequireHardLogIn public String cartCheck(final RedirectAttributes redirectModel) throws CommerceCartModificationException { SessionOverrideCheckoutFlowFacade.resetSessionOverrides(); if (!getCartFacade().hasEntries()) { LOG.info("Missing or empty cart"); // No session cart or empty session cart. Bounce back to the cart page. return REDIRECT_CART_URL; } if (validateCart(redirectModel)) { return REDIRECT_CART_URL; } // Redirect to the start of the checkout flow to begin the checkout process // We just redirect to the generic '/checkout' page which will actually select the checkout flow // to use. The customer is not necessarily logged in on this request, but will be forced to login // when they arrive on the '/checkout' page. return REDIRECT_PREFIX + "/checkout"; } @RequestMapping(value = "/getProductVariantMatrix", method = RequestMethod.GET) public String getProductVariantMatrix(@RequestParam("productCode") final String productCode, @RequestParam(value = "readOnly", required = false, defaultValue = "false") final String readOnly, final Model model) { final ProductData productData = productFacade.getProductForCodeAndOptions(productCode, Arrays.asList(ProductOption.BASIC, ProductOption.CATEGORIES, ProductOption.VARIANT_MATRIX_BASE, ProductOption.VARIANT_MATRIX_PRICE, ProductOption.VARIANT_MATRIX_MEDIA, ProductOption.VARIANT_MATRIX_STOCK, ProductOption.VARIANT_MATRIX_URL)); model.addAttribute("product", productData); model.addAttribute("readOnly", Boolean.valueOf(readOnly)); return ControllerConstants.Views.Fragments.Cart.ExpandGridInCart; } // This controller method is used to allow the site to force the visitor through a specified checkout flow. // If you only have a static configured checkout flow then you can remove this method. @RequestMapping(value = "/checkout/select-flow", method = RequestMethod.GET) @RequireHardLogIn public String initCheck(@RequestParam(value = "flow", required = false) final String flow, @RequestParam(value = "pci", required = false) final String pci) throws CommerceCartModificationException { SessionOverrideCheckoutFlowFacade.resetSessionOverrides(); if (!getCartFacade().hasEntries()) { LOG.info("Missing or empty cart"); // No session cart or empty session cart. Bounce back to the cart page. return REDIRECT_CART_URL; } // Override the Checkout Flow setting in the session if (StringUtils.isNotBlank(flow)) { final CheckoutFlowEnum checkoutFlow = enumerationService.getEnumerationValue(CheckoutFlowEnum.class, StringUtils.upperCase(flow)); SessionOverrideCheckoutFlowFacade.setSessionOverrideCheckoutFlow(checkoutFlow); } // Override the Checkout PCI setting in the session if (StringUtils.isNotBlank(pci)) { final CheckoutPciOptionEnum checkoutPci = enumerationService .getEnumerationValue(CheckoutPciOptionEnum.class, StringUtils.upperCase(pci)); SessionOverrideCheckoutFlowFacade.setSessionOverrideSubscriptionPciOption(checkoutPci); } // Redirect to the start of the checkout flow to begin the checkout process // We just redirect to the generic '/checkout' page which will actually select the checkout flow // to use. The customer is not necessarily logged in on this request, but will be forced to login // when they arrive on the '/checkout' page. return REDIRECT_PREFIX + "/checkout"; } @RequestMapping(value = "/update", method = RequestMethod.POST) public String updateCartQuantities(@RequestParam("entryNumber") final long entryNumber, final Model model, @Valid final UpdateQuantityForm form, final BindingResult bindingResult, final HttpServletRequest request, final RedirectAttributes redirectModel) throws CMSItemNotFoundException { if (bindingResult.hasErrors()) { for (final ObjectError error : bindingResult.getAllErrors()) { if ("typeMismatch".equals(error.getCode())) { GlobalMessages.addErrorMessage(model, "basket.error.quantity.invalid"); } else { GlobalMessages.addErrorMessage(model, error.getDefaultMessage()); } } } else if (getCartFacade().hasEntries()) { try { final CartModificationData cartModification = getCartFacade().updateCartEntry(entryNumber, form.getQuantity().longValue()); addFlashMessage(form, request, redirectModel, cartModification); final QuoteData quoteData = getCartFacade().getSessionCart().getQuoteData(); // Redirect to the cart page on update success so that the browser doesn't re-post again return quoteData != null ? String.format(REDIRECT_QUOTE_EDIT_URL, urlEncode(quoteData.getCode())) : REDIRECT_CART_URL; } catch (final CommerceCartModificationException ex) { LOG.warn("Couldn't update product with the entry number: " + entryNumber + ".", ex); } } // if could not update cart, display cart/quote page again with error return prepareCartUrl(model); } @Override protected void prepareDataForPage(final Model model) throws CMSItemNotFoundException { super.prepareDataForPage(model); if (!model.containsAttribute(VOUCHER_FORM)) { model.addAttribute(VOUCHER_FORM, new VoucherForm()); } // Because DefaultSiteConfigService.getProperty() doesn't set default boolean value for undefined property, // this property key was generated to use Config.getBoolean() method final String siteQuoteProperty = SITE_QUOTES_ENABLED .concat(getBaseSiteService().getCurrentBaseSite().getUid()); model.addAttribute("siteQuoteEnabled", Config.getBoolean(siteQuoteProperty, Boolean.FALSE)); model.addAttribute(WebConstants.BREADCRUMBS_KEY, resourceBreadcrumbBuilder.getBreadcrumbs("breadcrumb.cart")); model.addAttribute("pageType", PageType.CART.name()); } protected void addFlashMessage(final UpdateQuantityForm form, final HttpServletRequest request, final RedirectAttributes redirectModel, final CartModificationData cartModification) { if (cartModification.getQuantity() == form.getQuantity().longValue()) { // Success if (cartModification.getQuantity() == 0) { // Success in removing entry GlobalMessages.addFlashMessage(redirectModel, GlobalMessages.CONF_MESSAGES_HOLDER, "basket.page.message.remove"); } else { // Success in update quantity GlobalMessages.addFlashMessage(redirectModel, GlobalMessages.CONF_MESSAGES_HOLDER, "basket.page.message.update"); } } else if (cartModification.getQuantity() > 0) { // Less than successful GlobalMessages.addFlashMessage(redirectModel, GlobalMessages.ERROR_MESSAGES_HOLDER, "basket.page.message.update.reducedNumberOfItemsAdded.lowStock", new Object[] { XSSFilterUtil.filter(cartModification.getEntry().getProduct().getName()), Long.valueOf(cartModification.getQuantity()), form.getQuantity(), request.getRequestURL().append(cartModification.getEntry().getProduct().getUrl()) }); } else { // No more stock available GlobalMessages.addFlashMessage(redirectModel, GlobalMessages.ERROR_MESSAGES_HOLDER, "basket.page.message.update.reducedNumberOfItemsAdded.noStock", new Object[] { XSSFilterUtil.filter(cartModification.getEntry().getProduct().getName()), request.getRequestURL().append(cartModification.getEntry().getProduct().getUrl()) }); } } @SuppressWarnings("boxing") @ResponseBody @RequestMapping(value = "/updateMultiD", method = RequestMethod.POST) public CartData updateCartQuantitiesMultiD(@RequestParam("entryNumber") final Integer entryNumber, @RequestParam("productCode") final String productCode, final Model model, @Valid final UpdateQuantityForm form, final BindingResult bindingResult) { if (bindingResult.hasErrors()) { for (final ObjectError error : bindingResult.getAllErrors()) { if ("typeMismatch".equals(error.getCode())) { GlobalMessages.addErrorMessage(model, "basket.error.quantity.invalid"); } else { GlobalMessages.addErrorMessage(model, error.getDefaultMessage()); } } } else { try { final CartModificationData cartModification = getCartFacade() .updateCartEntry(getOrderEntryData(form.getQuantity(), productCode, entryNumber)); if (cartModification.getStatusCode().equals(SUCCESSFUL_MODIFICATION_CODE)) { GlobalMessages.addMessage(model, GlobalMessages.CONF_MESSAGES_HOLDER, cartModification.getStatusMessage(), null); } else if (!model.containsAttribute(ERROR_MSG_TYPE)) { GlobalMessages.addMessage(model, GlobalMessages.ERROR_MESSAGES_HOLDER, cartModification.getStatusMessage(), null); } } catch (final CommerceCartModificationException ex) { LOG.warn("Couldn't update product with the entry number: " + entryNumber + ".", ex); } } return getCartFacade().getSessionCart(); } @SuppressWarnings("boxing") protected OrderEntryData getOrderEntryData(final long quantity, final String productCode, final Integer entryNumber) { final OrderEntryData orderEntry = new OrderEntryData(); orderEntry.setQuantity(quantity); orderEntry.setProduct(new ProductData()); orderEntry.getProduct().setCode(productCode); orderEntry.setEntryNumber(entryNumber); return orderEntry; } @RequestMapping(value = "/save", method = RequestMethod.POST) @RequireHardLogIn public String saveCart(final SaveCartForm form, final BindingResult bindingResult, final RedirectAttributes redirectModel) throws CommerceSaveCartException { saveCartFormValidator.validate(form, bindingResult); if (bindingResult.hasErrors()) { for (final ObjectError error : bindingResult.getAllErrors()) { GlobalMessages.addFlashMessage(redirectModel, GlobalMessages.ERROR_MESSAGES_HOLDER, error.getCode()); } redirectModel.addFlashAttribute("saveCartForm", form); } else { final CommerceSaveCartParameterData commerceSaveCartParameterData = new CommerceSaveCartParameterData(); commerceSaveCartParameterData.setName(form.getName()); commerceSaveCartParameterData.setDescription(form.getDescription()); commerceSaveCartParameterData.setEnableHooks(true); try { final CommerceSaveCartResultData saveCartData = saveCartFacade .saveCart(commerceSaveCartParameterData); GlobalMessages.addFlashMessage(redirectModel, GlobalMessages.CONF_MESSAGES_HOLDER, "basket.save.cart.on.success", new Object[] { saveCartData.getSavedCartData().getName() }); } catch (final CommerceSaveCartException csce) { LOG.error(csce.getMessage(), csce); GlobalMessages.addFlashMessage(redirectModel, GlobalMessages.ERROR_MESSAGES_HOLDER, "basket.save.cart.on.error", new Object[] { form.getName() }); } } return REDIRECT_CART_URL; } @RequestMapping(value = "/export", method = RequestMethod.GET, produces = "text/csv") public String exportCsvFile(final HttpServletResponse response, final RedirectAttributes redirectModel) throws IOException { response.setHeader("Content-Disposition", "attachment;filename=cart.csv"); try (final StringWriter writer = new StringWriter()) { try { final List<String> headers = new ArrayList<String>(); headers.add(getMessageSource().getMessage("basket.export.cart.item.sku", null, getI18nService().getCurrentLocale())); headers.add(getMessageSource().getMessage("basket.export.cart.item.quantity", null, getI18nService().getCurrentLocale())); headers.add(getMessageSource().getMessage("basket.export.cart.item.name", null, getI18nService().getCurrentLocale())); headers.add(getMessageSource().getMessage("basket.export.cart.item.price", null, getI18nService().getCurrentLocale())); final CartData cartData = getCartFacade().getSessionCartWithEntryOrdering(false); csvFacade.generateCsvFromCart(headers, true, cartData, writer); StreamUtils.copy(writer.toString(), StandardCharsets.UTF_8, response.getOutputStream()); } catch (final IOException e) { LOG.error(e.getMessage(), e); GlobalMessages.addFlashMessage(redirectModel, GlobalMessages.ERROR_MESSAGES_HOLDER, "basket.export.cart.error", null); return REDIRECT_CART_URL; } } return null; } @RequestMapping(value = "/voucher/apply", method = RequestMethod.POST) public String applyVoucherAction(@Valid final VoucherForm form, final BindingResult bindingResult, final RedirectAttributes redirectAttributes) { try { if (bindingResult.hasErrors()) { redirectAttributes.addFlashAttribute("errorMsg", getMessageSource() .getMessage("text.voucher.apply.invalid.error", null, getI18nService().getCurrentLocale())); } else { voucherFacade.applyVoucher(form.getVoucherCode()); redirectAttributes.addFlashAttribute("successMsg", getMessageSource().getMessage("text.voucher.apply.applied.success", new Object[] { form.getVoucherCode() }, getI18nService().getCurrentLocale())); } } catch (final VoucherOperationException e) { redirectAttributes.addFlashAttribute(VOUCHER_FORM, form); redirectAttributes.addFlashAttribute("errorMsg", getMessageSource().getMessage( e.getMessage(), null, getMessageSource().getMessage("text.voucher.apply.invalid.error", null, getI18nService().getCurrentLocale()), getI18nService().getCurrentLocale())); if (LOG.isDebugEnabled()) { LOG.debug(e.getMessage(), e); } } return REDIRECT_CART_URL; } @RequestMapping(value = "/voucher/remove", method = RequestMethod.POST) public String removeVoucher(@Valid final VoucherForm form, final RedirectAttributes redirectModel) { try { voucherFacade.releaseVoucher(form.getVoucherCode()); } catch (final VoucherOperationException e) { GlobalMessages.addFlashMessage(redirectModel, GlobalMessages.ERROR_MESSAGES_HOLDER, "text.voucher.release.error", new Object[] { form.getVoucherCode() }); if (LOG.isDebugEnabled()) { LOG.debug(e.getMessage(), e); } } return REDIRECT_CART_URL; } public BaseSiteService getBaseSiteService() { return baseSiteService; } public void setBaseSiteService(final BaseSiteService baseSiteService) { this.baseSiteService = baseSiteService; } }