/*
* 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.BufferedReader;
import java.io.Reader;
import java.io.StringReader;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.digester.Digester;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
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.CommonService;
import com.salesmanager.core.service.common.model.IntegrationKeys;
import com.salesmanager.core.service.common.model.IntegrationProperties;
import com.salesmanager.core.service.merchant.ConfigurationRequest;
import com.salesmanager.core.service.merchant.ConfigurationResponse;
import com.salesmanager.core.service.merchant.MerchantService;
import com.salesmanager.core.util.CountryUtil;
import com.salesmanager.core.util.CurrencyUtil;
import com.salesmanager.core.util.DateUtil;
import com.salesmanager.core.util.LabelUtil;
import com.salesmanager.core.util.LanguageUtil;
import com.salesmanager.core.util.LogMerchantUtil;
public class USPSQuotesImpl implements ShippingQuotesModule {
private Logger log = Logger.getLogger(USPSQuotesImpl.class);
public Collection<ShippingOption> getShippingQuote(
ConfigurationResponse config, BigDecimal orderTotal,
Collection<PackageDetail> packages, Customer customer,
MerchantStore store, Locale locale) {
CoreModuleService cis = null;
StringBuffer xmlbuffer = new StringBuffer();
BufferedReader reader = null;
GetMethod httpget = null;
try {
CommonService cservice = (CommonService) ServiceFactory
.getService(ServiceFactory.CommonService);
String countrycode = CountryUtil.getCountryIsoCodeById(store
.getCountry());
cis = cservice.getModule(countrycode, "usps");
if (cis == null) {
log.error("Can't retreive an integration service [countryid "
+ store.getCountry() + " usps subtype 1]");
}
MerchantService service = (MerchantService) ServiceFactory
.getService(ServiceFactory.MerchantService);
ConfigurationRequest request_prefs = new ConfigurationRequest(store
.getMerchantId(),
ShippingConstants.MODULE_SHIPPING_RT_PKG_DOM_INT);
ConfigurationResponse vo_prefs = service
.getConfiguration(request_prefs);
String pack = (String) vo_prefs.getConfiguration("package-usps");
if (pack == null) {
log
.debug("Will assign packaging type 02 to USPS shipping for merchantid "
+ store.getMerchantId());
pack = "PARCEL";
}
ConfigurationRequest request = new ConfigurationRequest(store
.getMerchantId(), ShippingConstants.MODULE_SHIPPING_RT_CRED);
ConfigurationResponse vo = service.getConfiguration(request);
if (vo == null) {
throw new Exception("ConfigurationVO is null upsxml");
}
// if not shipping to USA
boolean domestic = true;
int shippingCountryId = customer.getCustomerCountryId();
if (shippingCountryId != Constants.US_COUNTRY_ID) {
domestic = false;
}
IntegrationKeys keys = (IntegrationKeys) vo
.getConfiguration("usps-keys");
if (keys == null) {
throw new Exception(
"Integration keys are null from configuration request usps");
}
String xmlheader = "<RateV3Request USERID=\"" + keys.getUserid()
+ "\">";
if (!domestic) {
xmlheader = "<IntlRateRequest USERID=\"" + keys.getUserid()
+ "\">";
}
StringBuffer xmldatabuffer = new StringBuffer();
Map countriesMap = (Map) RefCache.getAllcountriesmap(LanguageUtil
.getLanguageNumberCode(locale.getLanguage()));
Country customerCountry = (Country) countriesMap.get(customer
.getCustomerCountryId());
Iterator packagesIterator = packages.iterator();
double totalW = 0;
double totalH = 0;
double totalL = 0;
double totalG = 0;
double totalP = 0;
while (packagesIterator.hasNext()) {
PackageDetail detail = (PackageDetail) packagesIterator.next();
// need size in inch
double w = CurrencyUtil.getMeasure(detail.getShippingWidth(),
store, Constants.INCH_SIZE_UNIT);
double h = CurrencyUtil.getMeasure(detail.getShippingHeight(),
store, Constants.INCH_SIZE_UNIT);
double l = CurrencyUtil.getMeasure(detail.getShippingLength(),
store, Constants.INCH_SIZE_UNIT);
totalW = totalW + w;
totalH = totalH + h;
totalL = totalL + l;
// Girth = Length + (Width x 2) + (Height x 2)
double girth = l + (w * 2) + (h * 2);
totalG = totalG + girth;
// need weight in pounds
double p = CurrencyUtil.getWeight(detail.getShippingWeight(),
store, Constants.LB_WEIGHT_UNIT);
totalP = totalP + p;
}
BigDecimal convertedOrderTotal = CurrencyUtil.convertToCurrency(
orderTotal, store.getCurrency(),
Constants.CURRENCY_CODE_USD);
// calculate total shipping volume
// ship date is 3 days from here
Date newDate = DateUtil.addDaysToCurrentDate(3);
String shipDate = DateUtil.formatDateMonthString(newDate);
int i = 1;
// need pounds and ounces
int pounds = (int) totalP;
String ouncesString = String.valueOf(totalP - pounds);
int ouncesIndex = ouncesString.indexOf(".");
String ounces = "00";
if (ouncesIndex > -1) {
ounces = ouncesString.substring(ouncesIndex + 1);
}
String size = "REGULAR";
if (totalL + totalG <= 64) {
size = "REGULAR";
} else if (totalL + totalG <= 108) {
size = "LARGE";
} else {
size = "OVERSIZE";
}
/**
* Domestic <Package ID="1ST"> <Service>ALL</Service>
* <ZipOrigination>90210</ZipOrigination>
* <ZipDestination>96698</ZipDestination> <Pounds>8</Pounds>
* <Ounces>32</Ounces> <Container/> <Size>REGULAR</Size>
* <Machinable>true</Machinable> </Package>
*
* //MAXWEIGHT=70 lbs
*
*
* //domestic container default=VARIABLE whiteSpace=collapse
* enumeration=VARIABLE enumeration=FLAT RATE BOX enumeration=FLAT
* RATE ENVELOPE enumeration=LG FLAT RATE BOX
* enumeration=RECTANGULAR enumeration=NONRECTANGULAR
*
* //INTL enumeration=Package enumeration=Postcards or aerogrammes
* enumeration=Matter for the blind enumeration=Envelope
*
* Size May be left blank in situations that do not Size. Defined as
* follows: REGULAR: package plus girth is 84 inches or less; LARGE:
* package length plus girth measure more than 84 inches not more
* than 108 inches; OVERSIZE: package length plus girth is more than
* 108 but not 130 inches. For example: <Size>REGULAR</Size>
*
* International <Package ID="1ST"> <Machinable>true</Machinable>
* <MailType>Envelope</MailType> <Country>Canada</Country>
* <Length>0</Length> <Width>0</Width> <Height>0</Height>
* <ValueOfContents>250</ValueOfContents> </Package>
*
* <Package ID="2ND"> <Pounds>4</Pounds> <Ounces>3</Ounces>
* <MailType>Package</MailType> <GXG> <Length>46</Length>
* <Width>14</Width> <Height>15</Height> <POBoxFlag>N</POBoxFlag>
* <GiftFlag>N</GiftFlag> </GXG>
* <ValueOfContents>250</ValueOfContents> <Country>Japan</Country>
* </Package>
*/
xmldatabuffer.append("<Package ID=\"").append(i).append("\">");
// if domestic
// user selected services
// Map selectedintlservices =
// (Map)config.getConfiguration("service-global-usps");
// now get corresponding code
// Map allservices =
// com.salesmanager.core.util.ShippingUtil.buildServiceMapLabelByCode("usps",locale);
// TreeBidiMap bidiMap = new TreeBidiMap(allservices);
// Map invertedservicesmap = (Map)bidiMap.inverseBidiMap();
// Iterator selServices = selectedintlservices.keySet().iterator();
// while(selServices.hasNext()) {
// String serviceId = (String)selServices.next();
// String svc = (String)invertedservicesmap.get(serviceId);
if (domestic) {
xmldatabuffer.append("<Service>");
xmldatabuffer.append("ALL");
xmldatabuffer.append("</Service>");
xmldatabuffer.append("<ZipOrigination>");
xmldatabuffer.append(com.salesmanager.core.util.ShippingUtil
.trimPostalCode(store.getStorepostalcode()));
xmldatabuffer.append("</ZipOrigination>");
xmldatabuffer.append("<ZipDestination>");
xmldatabuffer.append(com.salesmanager.core.util.ShippingUtil
.trimPostalCode(customer.getCustomerPostalCode()));
xmldatabuffer.append("</ZipDestination>");
xmldatabuffer.append("<Pounds>");
xmldatabuffer.append(pounds);
xmldatabuffer.append("</Pounds>");
xmldatabuffer.append("<Ounces>");
xmldatabuffer.append(ounces);
xmldatabuffer.append("</Ounces>");
xmldatabuffer.append("<Container>");
xmldatabuffer.append(pack);
xmldatabuffer.append("</Container>");
xmldatabuffer.append("<Size>");
xmldatabuffer.append(size);
xmldatabuffer.append("</Size>");
xmldatabuffer.append("<ShipDate>");
xmldatabuffer.append(shipDate);
xmldatabuffer.append("</ShipDate>");
} else {
// if international
xmldatabuffer.append("<Pounds>");
xmldatabuffer.append(pounds);
xmldatabuffer.append("</Pounds>");
xmldatabuffer.append("<Ounces>");
xmldatabuffer.append(ounces);
xmldatabuffer.append("</Ounces>");
xmldatabuffer.append("<MailType>");
xmldatabuffer.append("Package");
xmldatabuffer.append("</MailType>");
xmldatabuffer.append("<ValueOfContents>");
xmldatabuffer.append(convertedOrderTotal);
xmldatabuffer.append("</ValueOfContents>");
xmldatabuffer.append("<Country>");
xmldatabuffer.append(customerCountry.getCountryName());
xmldatabuffer.append("</Country>");
}
// }
// if international & CXG
/*
* xmldatabuffer.append("<CXG>"); xmldatabuffer.append("<Length>");
* xmldatabuffer.append(""); xmldatabuffer.append("</Length>");
* xmldatabuffer.append("<Width>"); xmldatabuffer.append("");
* xmldatabuffer.append("</Width>");
* xmldatabuffer.append("<Height>"); xmldatabuffer.append("");
* xmldatabuffer.append("</Height>");
* xmldatabuffer.append("<POBoxFlag>"); xmldatabuffer.append("");
* xmldatabuffer.append("</POBoxFlag>");
* xmldatabuffer.append("<GiftFlag>"); xmldatabuffer.append("");
* xmldatabuffer.append("</GiftFlag>");
* xmldatabuffer.append("</CXG>");
*/
/*
* xmldatabuffer.append("<Width>"); xmldatabuffer.append(totalW);
* xmldatabuffer.append("</Width>");
* xmldatabuffer.append("<Length>"); xmldatabuffer.append(totalL);
* xmldatabuffer.append("</Length>");
* xmldatabuffer.append("<Height>"); xmldatabuffer.append(totalH);
* xmldatabuffer.append("</Height>");
* xmldatabuffer.append("<Girth>"); xmldatabuffer.append(totalG);
* xmldatabuffer.append("</Girth>");
*/
xmldatabuffer.append("</Package>");
String xmlfooter = "</RateRequest>";
if (!domestic) {
xmlfooter = "</IntlRateRequest>";
}
xmlbuffer.append(xmlheader.toString()).append(
xmldatabuffer.toString()).append(xmlfooter.toString());
log.debug("USPS QUOTE REQUEST " + xmlbuffer.toString());
String data = "";
IntegrationProperties props = (IntegrationProperties) config
.getConfiguration("usps-properties");
String host = cis.getCoreModuleServiceProdDomain();
String protocol = cis.getCoreModuleServiceProdProtocol();
String port = cis.getCoreModuleServiceProdPort();
String uri = cis.getCoreModuleServiceProdEnv();
if (props.getProperties1().equals(
String.valueOf(ShippingConstants.TEST_ENVIRONMENT))) {
host = cis.getCoreModuleServiceDevDomain();
protocol = cis.getCoreModuleServiceDevProtocol();
port = cis.getCoreModuleServiceDevPort();
uri = cis.getCoreModuleServiceDevEnv();
}
HttpClient client = new HttpClient();
String encoded = java.net.URLEncoder.encode(xmlbuffer.toString());
String completeUri = uri + "?API=RateV3&XML=" + encoded;
if (!domestic) {
completeUri = uri + "?API=IntlRate&XML=" + encoded;
}
// ?API=RateV3
httpget = new GetMethod(protocol + "://" + host + ":" + port
+ completeUri);
// RequestEntity entity = new
// StringRequestEntity(xmlbuffer.toString(),"text/plain","UTF-8");
// httpget.setRequestEntity(entity);
int result = client.executeMethod(httpget);
if (result != 200) {
log.error("Communication Error with usps quote " + result + " "
+ protocol + "://" + host + ":" + port + uri);
throw new Exception("USPS quote communication error " + result);
}
data = httpget.getResponseBodyAsString();
log.debug("usps quote response " + data);
UPSParsedElements parsed = new UPSParsedElements();
/**
* <RateV3Response> <Package ID="1ST">
* <ZipOrigination>44106</ZipOrigination>
* <ZipDestination>20770</ZipDestination>
*/
Digester digester = new Digester();
digester.push(parsed);
if (domestic) {
digester.addCallMethod("RateV3Response/Package/Error",
"setError", 0);
digester
.addObjectCreate(
"RateV3Response/Package/Postage",
com.salesmanager.core.entity.shipping.ShippingOption.class);
digester.addSetProperties("RateV3Response/Package/Postage",
"CLASSID", "optionId");
digester.addCallMethod(
"RateV3Response/Package/Postage/MailService",
"optionName", 0);
digester.addCallMethod(
"RateV3Response/Package/Postage/MailService",
"optionCode", 0);
digester.addCallMethod("RateV3Response/Package/Postage/Rate",
"optionPrice", 0);
digester
.addCallMethod(
"RateV3Response/Package/Postage/Commitment/CommitmentDate",
"estimatedNumberOfDays", 0);
digester.addSetNext("RateV3Response/Package/Postage",
"addOption");
} else {
digester.addCallMethod("IntlRateResponse/Package/Error",
"setError", 0);
digester
.addObjectCreate(
"IntlRateResponse/Package/Service",
com.salesmanager.core.entity.shipping.ShippingOption.class);
digester.addSetProperties("IntlRateResponse/Package/Service",
"ID", "optionId");
digester.addCallMethod(
"IntlRateResponse/Package/Service/SvcDescription",
"setOptionName", 0);
digester.addCallMethod(
"IntlRateResponse/Package/Service/SvcDescription",
"setOptionCode", 0);
digester.addCallMethod(
"IntlRateResponse/Package/Service/Postage",
"setOptionPriceText", 0);
digester.addCallMethod(
"IntlRateResponse/Package/Service/SvcCommitments",
"setEstimatedNumberOfDays", 0);
digester.addSetNext("IntlRateResponse/Package/Service",
"addOption");
}
// <?xml
// version="1.0"?><AddressValidationResponse><Response><TransactionReference><CustomerContext>SalesManager
// Data</CustomerContext><XpciVersion>1.0</XpciVersion></TransactionReference><ResponseStatusCode>0</ResponseStatusCode><ResponseStatusDescription>Failure</ResponseStatusDescription><Error><ErrorSeverity>Hard</ErrorSeverity><ErrorCode>10002</ErrorCode><ErrorDescription>The
// XML document is well formed but the document is not
// valid</ErrorDescription><ErrorLocation><ErrorLocationElementName>AddressValidationRequest</ErrorLocationElementName></ErrorLocation></Error></Response></AddressValidationResponse>
Reader xmlreader = new StringReader(data);
digester.parse(xmlreader);
if (!StringUtils.isBlank(parsed.getErrorCode())) {
log.error("Can't process UPS statusCode="
+ parsed.getErrorCode() + " message= "
+ parsed.getError());
return null;
}
if (!StringUtils.isBlank(parsed.getStatusCode())
&& !parsed.getStatusCode().equals("1")) {
LogMerchantUtil.log(store.getMerchantId(),
"Can't process UPS statusCode="
+ parsed.getStatusCode() + " message= "
+ parsed.getError());
log.error("Can't process UPS statusCode="
+ parsed.getStatusCode() + " message= "
+ parsed.getError());
return null;
}
if (parsed.getOptions() == null || parsed.getOptions().size() == 0) {
log.warn("No options returned from UPS");
return null;
}
String carrier = getShippingMethodDescription(locale);
// cost is in USD, need to do conversion
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() + "]");
}
}
}
LabelUtil labelUtil = LabelUtil.getInstance();
// Map serviceMap =
// com.salesmanager.core.util.ShippingUtil.buildServiceMap("usps",locale);
List options = parsed.getOptions();
Collection returnColl = null;
if (options != null && options.size() > 0) {
returnColl = new ArrayList();
// Map selectedintlservices =
// (Map)config.getConfiguration("service-global-usps");
// need to create a Map of LABEL - LABLEL
// Iterator servicesIterator =
// selectedintlservices.keySet().iterator();
// Map services = new HashMap();
// ResourceBundle bundle = ResourceBundle.getBundle("usps",
// locale);
// while(servicesIterator.hasNext()) {
// String key = (String)servicesIterator.next();
// String value =
// bundle.getString("shipping.quote.services.label." + key);
// services.put(value, key);
// }
Iterator it = options.iterator();
while (it.hasNext()) {
ShippingOption option = (ShippingOption) it.next();
option.setCurrency(Constants.CURRENCY_CODE_USD);
StringBuffer description = new StringBuffer();
description.append(option.getOptionName());
if (displayQuoteDeliveryTime == ShippingConstants.DISPLAY_RT_QUOTE_TIME) {
if (!StringUtils.isBlank(option
.getEstimatedNumberOfDays())) {
description.append(" (").append(
option.getEstimatedNumberOfDays()).append(
" ").append(
labelUtil.getText(locale,
"label.generic.days.lowercase"))
.append(")");
}
}
option.setDescription(description.toString());
// get currency
if (!option.getCurrency().equals(store.getCurrency())) {
option.setOptionPrice(CurrencyUtil.convertToCurrency(
option.getOptionPrice(), option.getCurrency(),
store.getCurrency()));
}
// if(!services.containsKey(option.getOptionCode())) {
// if(returnColl==null) {
// returnColl = new ArrayList();
// }
// returnColl.add(option);
// }
returnColl.add(option);
}
// if(options.size()==0) {
// CommonService.logServiceMessage(store.getMerchantId(),
// " none of the service code returned by UPS [" +
// selectedintlservices.keySet().toArray(new
// String[selectedintlservices.size()]) +
// "] for this shipping is in your selection list");
// }
}
return returnColl;
} catch (Exception e1) {
log.error(e1);
return null;
} finally {
if (reader != null) {
try {
reader.close();
} catch (Exception ignore) {
}
}
if (httpget != null) {
httpget.releaseConnection();
}
}
}
public String getShippingMethodDescription(Locale locale) {
return LabelUtil.getInstance().getText(locale, "module.usps");
}
public ConfigurationResponse getConfiguration(
MerchantConfiguration configurations, ConfigurationResponse vo)
throws Exception {
if (configurations.getConfigurationKey().equals(
ShippingConstants.MODULE_SHIPPING_RT_CRED)) {// handle
// credentials
if (!StringUtils.isBlank(configurations.getConfigurationValue1())) {
IntegrationKeys keys = ShippingUtil.getKeys(configurations
.getConfigurationValue1());
vo.addConfiguration("usps-keys", keys);
}
if (!StringUtils.isBlank(configurations.getConfigurationValue2())) {
IntegrationProperties props = ShippingUtil
.getProperties(configurations.getConfigurationValue2());
vo.addConfiguration("usps-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-usps", configurations
.getConfigurationValue());
}
// Global
if (!StringUtils.isBlank(configurations.getConfigurationValue2())) {
globalmap = new HashMap();
String intl = configurations.getConfigurationValue2();
StringTokenizer st = new StringTokenizer(intl, ";");
while (st.hasMoreTokens()) {
String token = st.nextToken();
globalmap.put(token, token);
}
vo.addConfiguration("service-global-usps", globalmap);
}
}
vo.addMerchantConfiguration(configurations);
return vo;
}
public void storeConfiguration(int merchantid, ConfigurationResponse vo, HttpServletRequest request)
throws Exception {
// implemented in ShippinguspsAction
}
}
class USPSParsedElements {
private String statusCode;
private String statusMessage;
private String error = "";
private String errorCode = "";
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;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
}