/* * Licensed to csti consulting * You may obtain a copy of the License at * * http://www.csticonsulting.com * Copyright (c) 2006-Aug 24, 2010 Consultation CS-TI inc. * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package com.salesmanager.core.module.impl.integration.shipping; import java.io.Reader; import java.io.StringReader; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.apache.commons.digester.Digester; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.methods.RequestEntity; import org.apache.commons.httpclient.methods.StringRequestEntity; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import com.salesmanager.core.constants.Constants; import com.salesmanager.core.constants.ShippingConstants; import com.salesmanager.core.entity.customer.Customer; import com.salesmanager.core.entity.merchant.MerchantConfiguration; import com.salesmanager.core.entity.merchant.MerchantStore; import com.salesmanager.core.entity.reference.CoreModuleService; import com.salesmanager.core.entity.reference.Country; import com.salesmanager.core.entity.shipping.PackageDetail; import com.salesmanager.core.entity.shipping.ShippingOption; import com.salesmanager.core.module.model.integration.ShippingQuotesModule; import com.salesmanager.core.service.ServiceFactory; import com.salesmanager.core.service.cache.RefCache; import com.salesmanager.core.service.common.model.IntegrationKeys; import com.salesmanager.core.service.common.model.IntegrationProperties; import com.salesmanager.core.service.merchant.ConfigurationResponse; import com.salesmanager.core.service.shipping.ShippingService; import com.salesmanager.core.util.CountryUtil; import com.salesmanager.core.util.CurrencyUtil; import com.salesmanager.core.util.LabelUtil; import com.salesmanager.core.util.LanguageUtil; import com.salesmanager.core.util.LogMerchantUtil; public class CanadaPostQuotesImpl implements ShippingQuotesModule { private Logger log = Logger.getLogger(CanadaPostQuotesImpl.class); public String getShippingMethodDescription(Locale locale) { return LabelUtil.getInstance().getText(locale, "module.canadapost"); } public Collection<ShippingOption> getShippingQuote( ConfigurationResponse config, BigDecimal orderTotal, Collection<PackageDetail> packages, Customer customer, MerchantStore store, Locale locale) { BigDecimal total = orderTotal; if (packages == null) { return null; } // only applies to Canada and US if (customer.getCustomerCountryId() != 38 && customer.getCustomerCountryId() != 223) { return null; } // supports en and fr String language = locale.getLanguage(); if (!language.equals(Constants.FRENCH_CODE) && !language.equals(Constants.ENGLISH_CODE)) { language = Constants.ENGLISH_CODE; } // get canadapost credentials if (config == null) { log .error("CanadaPostQuotesImp.getShippingQuote requires ConfigurationVO for key SHP_RT_CRED"); return null; } // if store is not CAD if (!store.getCurrency().equals(Constants.CURRENCY_CODE_CAD)) { total = CurrencyUtil.convertToCurrency(total, store.getCurrency(), Constants.CURRENCY_CODE_CAD); } PostMethod httppost = null; CanadaPostParsedElements canadaPost = null; try { int icountry = store.getCountry(); String country = CountryUtil.getCountryIsoCodeById(icountry); ShippingService sservice = (ShippingService) ServiceFactory .getService(ServiceFactory.ShippingService); CoreModuleService cms = sservice.getRealTimeQuoteShippingService( country, "canadapost"); IntegrationKeys keys = (IntegrationKeys) config .getConfiguration("canadapost-keys"); IntegrationProperties props = (IntegrationProperties) config .getConfiguration("canadapost-properties"); if (cms == null) { // throw new // Exception("Central integration services not configured for " // + PaymentConstants.PAYMENT_PSIGATENAME + " and country id " + // origincountryid); log .error("CoreModuleService not configured for canadapost and country id " + icountry); return null; } String host = cms.getCoreModuleServiceProdDomain(); String protocol = cms.getCoreModuleServiceProdProtocol(); String port = cms.getCoreModuleServiceProdPort(); String url = cms.getCoreModuleServiceProdEnv(); if (props.getProperties1().equals( String.valueOf(ShippingConstants.TEST_ENVIRONMENT))) { host = cms.getCoreModuleServiceDevDomain(); protocol = cms.getCoreModuleServiceDevProtocol(); port = cms.getCoreModuleServiceDevPort(); url = cms.getCoreModuleServiceDevEnv(); } // accept KG and CM StringBuffer request = new StringBuffer(); request.append("<?xml version=\"1.0\" ?>"); request.append("<eparcel>"); request.append("<language>").append(language).append("</language>"); request.append("<ratesAndServicesRequest>"); request.append("<merchantCPCID>").append(keys.getUserid()).append( "</merchantCPCID>"); request.append("<fromPostalCode>").append( com.salesmanager.core.util.ShippingUtil .trimPostalCode(store.getStorepostalcode())) .append("</fromPostalCode>"); request.append("<turnAroundTime>").append("24").append( "</turnAroundTime>"); request.append("<itemsPrice>").append( CurrencyUtil.displayFormatedAmountNoCurrency(total, "CAD")) .append("</itemsPrice>"); request.append("<lineItems>"); Iterator packageIterator = packages.iterator(); while (packageIterator.hasNext()) { PackageDetail pack = (PackageDetail) packageIterator.next(); request.append("<item>"); request.append("<quantity>").append(pack.getShippingQuantity()) .append("</quantity>"); request.append("<weight>").append( String.valueOf(CurrencyUtil.getWeight(pack .getShippingWeight(), store, Constants.KG_WEIGHT_UNIT))).append("</weight>"); request.append("<length>").append( String.valueOf(CurrencyUtil.getMeasure(pack .getShippingLength(), store, Constants.CM_SIZE_UNIT))).append("</length>"); request.append("<width>").append( String.valueOf(CurrencyUtil.getMeasure(pack .getShippingWidth(), store, Constants.CM_SIZE_UNIT))).append("</width>"); request.append("<height>").append( String.valueOf(CurrencyUtil.getMeasure(pack .getShippingHeight(), store, Constants.CM_SIZE_UNIT))).append("</height>"); request.append("<description>").append(pack.getProductName()) .append("</description>"); request.append("<readyToShip/>"); request.append("</item>"); } Country c = null; Map countries = (Map) RefCache.getAllcountriesmap(LanguageUtil .getLanguageNumberCode(locale.getLanguage())); c = (Country) countries.get(store.getCountry()); request.append("</lineItems>"); request.append("<city>").append(customer.getCustomerCity()).append( "</city>"); request.append("<provOrState>").append(customer.getShippingSate()) .append("</provOrState>"); Map cs = (Map) RefCache.getAllcountriesmap(LanguageUtil .getLanguageNumberCode(locale.getLanguage())); Country customerCountry = (Country) cs.get(customer .getCustomerCountryId()); request.append("<country>") .append(customerCountry.getCountryName()).append( "</country>"); request.append("<postalCode>").append( com.salesmanager.core.util.ShippingUtil .trimPostalCode(customer.getCustomerPostalCode())) .append("</postalCode>"); request.append("</ratesAndServicesRequest>"); request.append("</eparcel>"); /** * <?xml version="1.0" ?> <eparcel> * <!--********************************--> <!-- Prefered language * for the --> <!-- response (FR/EN) (optional) --> * <!--********************************--> <language>en</language> * * <ratesAndServicesRequest> * <!--**********************************--> <!-- Merchant * Identification assigned --> <!-- by Canada Post --> <!-- --> <!-- * Note: Use 'CPC_DEMO_HTML' or ask --> <!-- our Help Desk to change * your --> <!-- profile if you want HTML to be --> <!-- returned to * you --> <!--**********************************--> <merchantCPCID> * CPC_DEMO_XML </merchantCPCID> * * <!--*********************************--> <!--Origin Postal Code * --> <!--This parameter is optional --> * <!--*********************************--> * <fromPostalCode>m1p1c0</fromPostalCode> * * <!--**********************************--> <!-- Turn Around Time * (hours) --> <!-- This parameter is optional --> * <!--**********************************--> <turnAroundTime> 24 * </turnAroundTime> * * <!--**********************************--> <!-- Total amount in $ * of the items --> <!-- for insurance calculation --> <!-- This * parameter is optional --> * <!--**********************************--> * <itemsPrice>0.00</itemsPrice> * * <!--**********************************--> <!-- List of items in * the shopping --> <!-- cart --> <!-- Each item is defined by : --> * <!-- - quantity (mandatory) --> <!-- - size (mandatory) --> <!-- * - weight (mandatory) --> <!-- - description (mandatory) --> <!-- * - ready to ship (optional) --> * <!--**********************************--> <lineItems> <item> * <quantity> 1 </quantity> <weight> 1.491 </weight> <length> 1 * </length> <width> 1 </width> <height> 1 </height> <description> * KAO Diskettes </description> </item> * * <item> <quantity> 1 </quantity> <weight> 1.5 </weight> <length> * 20 </length> <width> 30 </width> <height> 20 </height> * <description> My Ready To Ship Item</description> * <!--**********************************************--> <!-- By * adding the 'readyToShip' tag, Sell Online --> <!-- will not pack * this item in the boxes --> <!-- defined in the merchant profile. * --> <!-- Instead, this item will be shipped in its --> <!-- * original box: 1.5 kg and 20x30x20 cm --> * <!--**********************************************--> * <readyToShip/> </item> </lineItems> * * <!--********************************--> <!-- City where the * parcel will be --> <!-- shipped to --> * <!--********************************--> <city> </city> * * <!--********************************--> <!-- Province (Canada) or * State (US)--> <!-- where the parcel will be --> <!-- shipped to * --> <!--********************************--> <provOrState> * Wisconsin </provOrState> * * <!--********************************--> <!-- Country or ISO * Country code --> <!-- where the parcel will be --> <!-- shipped * to --> <!--********************************--> <country> CANADA * </country> * * <!--********************************--> <!-- Postal Code (or ZIP) * where the --> <!-- parcel will be shipped to --> * <!--********************************--> <postalCode> * H3K1E5</postalCode> </ratesAndServicesRequest> </eparcel> **/ log.debug("canadapost request " + request.toString()); HttpClient client = new HttpClient(); StringBuilder u = new StringBuilder().append(protocol).append("://").append(host).append(":").append(port); if(!StringUtils.isBlank(url)) { u.append(url); } log.debug("Canadapost URL " + u.toString()); httppost = new PostMethod(u.toString()); RequestEntity entity = new StringRequestEntity(request.toString(), "text/plain", "UTF-8"); httppost.setRequestEntity(entity); int result = client.executeMethod(httppost); if (result != 200) { log.error("Communication Error with canadapost " + protocol + "://" + host + ":" + port + url); throw new Exception("Communication Error with canadapost " + protocol + "://" + host + ":" + port + url); } String stringresult = httppost.getResponseBodyAsString(); log.debug("canadapost response " + stringresult); canadaPost = new CanadaPostParsedElements(); Digester digester = new Digester(); digester.push(canadaPost); digester.addCallMethod( "eparcel/ratesAndServicesResponse/statusCode", "setStatusCode", 0); digester.addCallMethod( "eparcel/ratesAndServicesResponse/statusMessage", "setStatusMessage", 0); digester.addObjectCreate( "eparcel/ratesAndServicesResponse/product", com.salesmanager.core.entity.shipping.ShippingOption.class); digester.addSetProperties( "eparcel/ratesAndServicesResponse/product", "sequence", "optionId"); digester.addCallMethod( "eparcel/ratesAndServicesResponse/product/shippingDate", "setShippingDate", 0); digester.addCallMethod( "eparcel/ratesAndServicesResponse/product/deliveryDate", "setDeliveryDate", 0); digester.addCallMethod( "eparcel/ratesAndServicesResponse/product/name", "setOptionName", 0); digester.addCallMethod( "eparcel/ratesAndServicesResponse/product/rate", "setOptionPriceText", 0); digester.addSetNext("eparcel/ratesAndServicesResponse/product", "addOption"); /** * response * * <?xml version="1.0" ?> <!DOCTYPE eparcel (View Source for full * doctype...)> - <eparcel> - <ratesAndServicesResponse> * <statusCode>1</statusCode> <statusMessage>OK</statusMessage> * <requestID>1769506</requestID> <handling>0.0</handling> * <language>0</language> - <product id="1040" sequence="1"> * <name>Priority Courier</name> <rate>38.44</rate> * <shippingDate>2008-12-22</shippingDate> * <deliveryDate>2008-12-23</deliveryDate> * <deliveryDayOfWeek>3</deliveryDayOfWeek> * <nextDayAM>true</nextDayAM> <packingID>P_0</packingID> </product> * - <product id="1020" sequence="2"> <name>Expedited</name> * <rate>16.08</rate> <shippingDate>2008-12-22</shippingDate> * <deliveryDate>2008-12-23</deliveryDate> * <deliveryDayOfWeek>3</deliveryDayOfWeek> * <nextDayAM>false</nextDayAM> <packingID>P_0</packingID> * </product> - <product id="1010" sequence="3"> * <name>Regular</name> <rate>16.08</rate> * <shippingDate>2008-12-22</shippingDate> * <deliveryDate>2008-12-29</deliveryDate> * <deliveryDayOfWeek>2</deliveryDayOfWeek> * <nextDayAM>false</nextDayAM> <packingID>P_0</packingID> * </product> - <packing> <packingID>P_0</packingID> - <box> * <name>Small Box</name> <weight>1.691</weight> * <expediterWeight>1.691</expediterWeight> <length>25.0</length> * <width>17.0</width> <height>16.0</height> - <packedItem> * <quantity>1</quantity> <description>KAO Diskettes</description> * </packedItem> </box> - <box> <name>My Ready To Ship Item</name> * <weight>2.0</weight> <expediterWeight>1.5</expediterWeight> * <length>30.0</length> <width>20.0</width> <height>20.0</height> - * <packedItem> <quantity>1</quantity> <description>My Ready To Ship * Item</description> </packedItem> </box> </packing> - * <shippingOptions> <insurance>No</insurance> * <deliveryConfirmation>Yes</deliveryConfirmation> * <signature>No</signature> </shippingOptions> <comment /> * </ratesAndServicesResponse> </eparcel> - <!-- END_OF_EPARCEL --> */ Reader reader = new StringReader(stringresult); digester.parse(reader); } catch (Exception e) { log.error(e); } finally { if (httppost != null) { httppost.releaseConnection(); } } if (canadaPost == null || canadaPost.getStatusCode() == null) { return null; } if (canadaPost.getStatusCode().equals("-6") || canadaPost.getStatusCode().equals("-7")) { LogMerchantUtil.log(store.getMerchantId(), "Can't process CanadaPost statusCode=" + canadaPost.getStatusCode() + " message= " + canadaPost.getStatusMessage()); } if (!canadaPost.getStatusCode().equals("1")) { log.error("An error occured with canadapost request (code-> " + canadaPost.getStatusCode() + " message-> " + canadaPost.getStatusMessage() + ")"); return null; } String carrier = getShippingMethodDescription(locale); // cost is in CAD, need to do conversion boolean requiresCurrencyConversion = false; String storeCurrency = store.getCurrency(); if (!storeCurrency.equals(Constants.CURRENCY_CODE_CAD)) { requiresCurrencyConversion = true; } /** Details on whit RT quote information to display **/ MerchantConfiguration rtdetails = config .getMerchantConfiguration(ShippingConstants.MODULE_SHIPPING_DISPLAY_REALTIME_QUOTES); int displayQuoteDeliveryTime = ShippingConstants.NO_DISPLAY_RT_QUOTE_TIME; if (rtdetails != null) { if (!StringUtils.isBlank(rtdetails.getConfigurationValue1())) {// display // or // not // quotes try { displayQuoteDeliveryTime = Integer.parseInt(rtdetails .getConfigurationValue1()); } catch (Exception e) { log.error("Display quote is not an integer value [" + rtdetails.getConfigurationValue1() + "]"); } } } /**/ List options = canadaPost.getOptions(); if (options != null) { Iterator i = options.iterator(); while (i.hasNext()) { ShippingOption option = (ShippingOption) i.next(); option.setCurrency(store.getCurrency()); StringBuffer description = new StringBuffer(); description.append(option.getOptionName()); if (displayQuoteDeliveryTime == ShippingConstants.DISPLAY_RT_QUOTE_TIME) { description.append(" (").append(option.getDeliveryDate()) .append(")"); } option.setDescription(description.toString()); if (requiresCurrencyConversion) { option.setOptionPrice(CurrencyUtil.convertToCurrency(option .getOptionPrice(), Constants.CURRENCY_CODE_CAD, store.getCurrency())); } // System.out.println(option.getOptionPrice().toString()); } } return options; } public ConfigurationResponse getConfiguration( MerchantConfiguration configurations, ConfigurationResponse vo) throws Exception { if (configurations.getConfigurationKey().equals( ShippingConstants.MODULE_SHIPPING_RT_CRED)) {// handle // credentials if (!StringUtils.isBlank(configurations.getConfigurationValue2())) { IntegrationKeys keys = ShippingUtil.getKeys(configurations .getConfigurationValue1()); vo.addConfiguration("canadapost-keys", keys); } if (!StringUtils.isBlank(configurations.getConfigurationValue2())) { IntegrationProperties props = ShippingUtil .getProperties(configurations.getConfigurationValue2()); vo.addConfiguration("canadapost-properties", props); } } if (configurations.getConfigurationKey().equals( ShippingConstants.MODULE_SHIPPING_RT_PKG_DOM_INT)) {// handle // packages // & // services Map domesticmap = null; Map globalmap = null; // PKGOPTIONS if (!StringUtils.isBlank(configurations.getConfigurationValue())) { vo.addConfiguration("package-canadapost", configurations .getConfigurationValue()); } } vo.addMerchantConfiguration(configurations); return vo; } public void storeConfiguration(int merchantid, ConfigurationResponse vo, HttpServletRequest request) throws Exception { // TODO Auto-generated method stub } } class CanadaPostParsedElements { private String statusCode; private String statusMessage; private List options = new ArrayList(); public void addOption(ShippingOption option) { options.add(option); } public List getOptions() { return options; } public String getStatusCode() { return statusCode; } public void setStatusCode(String statusCode) { this.statusCode = statusCode; } public String getStatusMessage() { return statusMessage; } public void setStatusMessage(String statusMessage) { this.statusMessage = statusMessage; } }