• Welcome to KonaKart Community Forum. Please login or sign up.
 

Shipping Module & Fulfillment Services

Started by sfsylz, June 12, 2007, 09:06:28 am

Previous topic - Next topic

sfsylz

I want to get a shipping quote from a company via XML Web Services. The return values contain multiple options: < Response >

   < ErrorCode > 0 < /ErrorCode >
   < ErrorText > success < /ErrorText >

   < Option >
       < Description > USPS First Class Mail < /Description >
       < DeliveryTime > 2-5 days < /DeliveryTime >
       < Price > 1.29 < /Price >
   < /Option >

   < Option >
       < Description > UPS Ground < /Description> >
       < DeliveryTime > 2-4 days < /DeliveryTime >
       < Price > 7.90 < /Price >
   < /Option >

   < Option >
       < Description > Fedex guaranteed 3-day < /Description> >
       < DeliveryTime > 3 days < /DeliveryTime >
       < Price > 8.24 < /Price >
   < /Option >

< Response >

I would like to populate the shipping page with this information, but the Shipping Modules only seem to return one ShippingQuote. Am I going to have to implement different modules for each of the types and return null if the option isn't available?

Also, I am trying to integrate with a fulfillment company and I am ending up modifying the WorldPay callback class to automatically send the order to the fulfillment company. Is there a modular structure to fulfillment that I may have missed?

Cheers,
Steve

paolo

Hi Steve,

Yes, the way to achieve this is to create a shipping module for each type. I suggest creating a base class that contains most of the code, and then making a simple super class for each type. If the option isn't available, the module should throw a KKException.

For the fulfillment, another more loosely coupled way of doing it would be to configure KonaKart to send you an email whenever an order changes state. You have full control over the template of the email contents, so you can automatically parse the email on its arrival to send off instructions to the fulfilment company. If you had multiple payment methods, this way would work without having to customise each payment method.

Cheers,

Paolo

sfsylz

Paolo,

I am throwing a KKException from within the getQuote() method on each shipping module, but this doesn't seem to be handled because I am getting a null pointer exception on a jsp tag. "Could not find a match for the shipping method." is the message that I send along with the KKException.  Am I throwing the exception in the right place - I need the Order object to create the shipping quote - so I am not able to check it in isAvailable()...  Any ideas?

INFO   | jvm 1    | 2007/06/12 10:35:34 | 12-Jun 10:35:34 INFO  (ShippingMgr.java:getShippingQuotes:481) Called the getQuote method on module com.konakart.bl.modules.shipping.uspsprioritymail.USPSPriorityMail. The module isn't available because of the following problem: Could not find a match for the shipping method.
INFO   | jvm 1    | 2007/06/12 10:35:34 | 12-Jun 10:35:34 ERROR (ApplicationDispatcher.java:invoke:704) Servlet.service() for servlet jsp threw exception
INFO   | jvm 1    | 2007/06/12 10:35:34 | java.lang.NullPointerException
INFO   | jvm 1    | 2007/06/12 10:35:34 |       at org.apache.struts.taglib.html.RadioTag.renderRadioElement(RadioTag.java:222)
INFO   | jvm 1    | 2007/06/12 10:35:34 |       at org.apache.struts.taglib.html.RadioTag.doStartTag(RadioTag.java:166)
INFO   | jvm 1    | 2007/06/12 10:35:34 |       at org.apache.jsp.WEB_002dINF.jsp.CatalogCheckoutDeliveryBody_jsp._jspService(CatalogCheckoutDeliveryBody_jsp.java:387)
INFO   | jvm 1    | 2007/06/12 10:35:34 |       at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:97)
INFO   | jvm 1    | 2007/06/12 10:35:34 |       at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
INFO   | jvm 1    | 2007/06/12 10:35:34 |       at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:334)
INFO   | jvm 1    | 2007/06/12 10:35:34 |       at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:314)
INFO   | jvm 1    | 2007/06/12 10:35:34 |       at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:264)
INFO   | jvm 1    | 2007/06/12 10:35:34 |       at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
INFO   | jvm 1    | 2007/06/12 10:35:34 |       at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
INFO   | jvm 1    | 2007/06/12 10:35:34 |       at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
INFO   | jvm 1    | 2007/06/12 10:35:34 |       at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:672)
INFO   | jvm 1    | 2007/06/12 10:35:34 |       at org.apache.catalina.core.ApplicationDispatcher.doInclude(ApplicationDispatcher.java:574)
                                                                                                            3038,1        90%

paolo

Hi Steve,

The way that it works is that when you throw an exception, the quote from that shipping module isn't added to the array of ShippingQuote objects returned by the getShippingQuotes() method. It is logged at INFO level which is why you are seeing it in the log.

The JSP is probably throwing an exception because there is some required attribute set to null in the quotes that it is receiving. I'd check the tag that it is complaining about and then try to figure out why the data it requires, isn't present in the ShippingQuote object.

-Paolo


sfsylz

That seems to have fixed it. Now I'm having trouble with the Order total page.

I have tried adding a scale of 2 to the BigDecimal, but still am getting this exception:

Exception Cause = java.lang.NullPointerException
at java.math.BigDecimal.matchScale(BigDecimal.java:3191)
at java.math.BigDecimal.add(BigDecimal.java:1007)
at com.konakart.app.Order.calculateTotals(Order.java:746)
at com.konakart.bl.modules.payment.PaymentMgr.getPaymentGateways(PaymentMgr.java:192)
at com.konakart.app.KKEng.getPaymentGateways(KKEng.java:1308)
at com.konakart.app.KKEng.getPaymentGateways(KKEng.java:63)
at com.konakart.al.OrderMgr.createPaymentGatewayList(OrderMgr.java:413)
at com.konakart.actions.CheckoutPaymentAction.execute(CheckoutPaymentAction.java:71)
at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:431)
at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:236)
at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1196)
at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:709)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:672)
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:463)
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:398)
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:301)
at org.apache.struts.action.RequestProcessor.doForward(RequestProcessor.java:1085)
at org.apache.struts.tiles.TilesRequestProcessor.doForward(TilesRequestProcessor.java:263)
at org.apache.struts.action.RequestProcessor.processForwardConfig(RequestProcessor.java:398)
at org.apache.struts.tiles.TilesRequestProcessor.processForwardConfig(TilesRequestProcessor.java:318)
at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:241)
at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1196)
at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:709)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:252)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:173)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:178)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:126)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:105)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:107)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:148)
at org.apache.jk.server.JkCoyoteHandler.invoke(JkCoyoteHandler.java:199)
at org.apache.jk.common.HandlerRequest.invoke(HandlerRequest.java:282)
at org.apache.jk.common.ChannelSocket.invoke(ChannelSocket.java:767)
at org.apache.jk.common.ChannelSocket.processConnection(ChannelSocket.java:697)
at org.apache.jk.common.ChannelSocket$SocketConnection.runIt(ChannelSocket.java:889)
at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:684)
at java.lang.Thread.run(Thread.java:595)

Ideas?

Cheers,
Steve

paolo

Hi Steve,

Could you ensure that the ShippingQuote object has the "totalExTax" and the "tax" attributes not set to null. If you have no tax, just set tax to a new BigDecimal(0).

-Paolo

sfsylz

I wasn't setting the tax. Hopefully this is the last question this time around. I am modifying the WorldPayAction to inform the fulfillment company of the transaction. But when I go to retrieve the order it isn't returning anything (null). Is there something that I'm missing here?  This code should seem pretty familiar.

When I try to access order in sendToKunaki(order) - I get a null pointer exception.

Ideas?

if (SUCCESSFUL_TRANSACTION_VALUE.equalsIgnoreCase(transactionType))
                {
                    // status : 2 = Processing
                    comment = ORDER_HISTORY_COMMENT_OK + transactionId;
                    kkAppEng.getEng().changeOrderStatus(sessionId, orderId, 5, sendEmail, comment);
                     
                    //If the order payment was approved we send the request for fulfillment to the fulfillment company.
                    //this.fulfillTheOrder(orderId);
                    //--------------------------------------------------------
                    log.info("Retrieving the order with sessionId: " + sessionId + " orderId: " + orderId + " and languageId: 1");
                    OrderIf order = kkAppEng.getEng().getOrder(sessionId, orderId, 1);
                    if(this.sendToKunaki(order))
                       kkAppEng.getEng().changeOrderStatus(sessionId, orderId, 7, false, "Sent to the fulfillment center");
                   
//                  If the order payment was approved we update the inventory
                    kkAppEng.getEng().updateInventory(sessionId, orderId);
                   
                    //--------------------------------------------------------
                    // If we expect no more communication from WorldPay for this order we can
                    // delete the SecretKey
                    kkAppEng.getEng().deleteOrderIdForSecretKey(secretKey);
                    emailSubject = EMAIL_SUBJECT_OK;

paolo

Hi Steve,

The problem is to do with security. The sessionId used by the WorldPayAction doesn't have the privileges to retrieve an order that doesn't belong to it. i.e. You can't log into the KK engine as Fred Smith and retrieve an order for Peter Jones. You can only retrieve orders that belong to you.

In order to get back the order, you really need to use the Admin API since this allows you to log in and get back any order. You've probably not used this API before so here is an example of what you need to do (note, it's only an example without null checks etc.):


        /*
         * Get an instance of the Admin engine
         */
        Class engineClass = Class.forName("com.konakartadmin.bl.KKAdmin");
        KKAdminIf eng = (KKAdminIf) engineClass.newInstance();

        /*
         * Login with admin app credentials
         */
        String sessionId = eng.login(AdminAppUser, AdminAppPassword);

         /*
         * Search for the Order id = 1
         */
       AdminOrderSearch aos = new AdminOrderSearch();
       aos.setOrderId(1);
       AdminOrderSearchResult orderSearchResult = eng.getOrders(sessionId, aos, 0, 1, -1);
       AdminOrder adminOrder = orderSearchResult.getOrders()[0]; 




sfsylz

I have done that (setting the orderId instead of a 1) and the shippingInfo and the product array on the returned AdminOrder object are null.

All I'm getting for stack traces are null pointer exceptions when I try to access these objects.

Steve

paolo

Hi Steve,

Ok, I see that. The Admin App uses the Admin App API and so far we haven't required any more detail in that object, which is why it isn't fully populated. We'll add this to the ToDo list.

What we do tend to use in the Admin App is the getHtml() call, that uses the velocity templates to return some HTML. You can see examples by clicking on the Invoice or Packing Slip buttons after having selected an order. Another item on the ToDo list is to make this mechanism easily expandable, allowing you to write new templates that will automatically get picked up by the call. For now however, you have two options:

1)You could modify the packing slip template (OrderPackingList_en.vm) to generate the exact HTML that you require, which can then be sent directly to the fulfillment company. Or modify it so that it is easy to parse.
2)You could use the HTML returned by calling the Invoice template and parse it to retrieve the information that you require.

An example of the getHtml() call is :

//           public static final int HTML_ORDER_DETAIL = 1;
//           public static final int HTML_ORDER_INVOICE = 2;
//           public static final int HTML_ORDER_PACKING_LIST = 3;
//
            String html = eng.getHtml(sessionId, KonakartAdminConstants.HTML_ORDER_PACKING_LIST, 1, -1, null);

You can find the Admin App API Javadoc at http://www.konakart.com/javadoc/admin/

-Paolo