View Javadoc
1   /*
2    * Copyright 2006-2009 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.ole.module.purap.document.web.struts;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.apache.commons.lang.math.RandomUtils;
20  import org.apache.struts.action.ActionForm;
21  import org.apache.struts.action.ActionForward;
22  import org.apache.struts.action.ActionMapping;
23  import org.kuali.ole.module.purap.PurapKeyConstants;
24  import org.kuali.ole.module.purap.PurapPropertyConstants;
25  import org.kuali.ole.module.purap.businessobject.PurchaseOrderItem;
26  import org.kuali.ole.module.purap.document.PurchaseOrderDocument;
27  import org.kuali.ole.module.purap.document.service.PurchaseOrderService;
28  import org.kuali.ole.module.purap.util.ElectronicInvoiceUtils;
29  import org.kuali.ole.sys.OLEConstants;
30  import org.kuali.ole.sys.context.SpringContext;
31  import org.kuali.ole.vnd.businessobject.PaymentTermType;
32  import org.kuali.rice.core.api.datetime.DateTimeService;
33  import org.kuali.rice.core.api.util.RiceConstants;
34  import org.kuali.rice.core.api.util.type.KualiDecimal;
35  import org.kuali.rice.kns.web.struts.action.KualiAction;
36  import org.kuali.rice.krad.exception.AuthorizationException;
37  import org.kuali.rice.krad.service.DocumentService;
38  import org.kuali.rice.krad.service.KRADServiceLocatorWeb;
39  import org.kuali.rice.krad.util.GlobalVariables;
40  import org.kuali.rice.krad.util.KRADUtils;
41  
42  import javax.servlet.ServletOutputStream;
43  import javax.servlet.http.HttpServletRequest;
44  import javax.servlet.http.HttpServletResponse;
45  import java.io.ByteArrayOutputStream;
46  import java.io.StringBufferInputStream;
47  import java.util.List;
48  
49  /**
50   * Struts Action for printing Purap documents outside of a document action
51   */
52  public class ElectronicInvoiceTestAction extends KualiAction {
53      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ElectronicInvoiceTestAction.class);
54  
55      private static final String AREA_C0DE = "areaCode";
56      private static final String PHONE_NUMBER = "phoneNumber";
57  
58      protected static volatile DocumentService documentService;
59  
60      /**
61       * @see org.kuali.rice.kns.web.struts.action.KualiAction#execute(org.apache.struts.action.ActionMapping,
62       *      org.apache.struts.action.ActionForm, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
63       */
64      @Override
65      public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
66          // TODO Uncomment the following code. This method doesn't need override.
67          // It's here as a temp get-around for a bug complaining method "generate" cannot be found
68          String methodToCall = findMethodToCall(form, request);
69          if (StringUtils.equals(methodToCall, "generate")) {
70              return generate(mapping, form, request, response);
71          }
72          return mapping.findForward(OLEConstants.MAPPING_BASIC);
73      }
74  
75      /**
76       * @see org.kuali.rice.kns.web.struts.action.KualiAction#checkAuthorization(org.apache.struts.action.ActionForm, java.lang.String)
77       *      <p/>
78       *      Only allow users to test eInvoicing in the test environment
79       */
80      @Override
81      protected void checkAuthorization(ActionForm form, String methodToCall) throws AuthorizationException {
82          if (KRADUtils.isProductionEnvironment()) {
83              //this process is not available for production
84              throw new AuthorizationException(GlobalVariables.getUserSession().getPerson().getPrincipalName(), methodToCall, this.getClass().getSimpleName());
85          }
86      }
87  
88      /**
89       * Generates Electronic Invoice xml file from a Purchase Order, for testing purpose only.
90       */
91      public ActionForward generate(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
92          checkAuthorization(form, "");
93  
94          ElectronicInvoiceTestForm testForm = (ElectronicInvoiceTestForm) form;
95          String poDocNumber = testForm.getPoDocNumber();
96          LOG.info("Generating Electronic Invoice XML file for Purchase Order Document " + poDocNumber);
97          PurchaseOrderService poService = SpringContext.getBean(PurchaseOrderService.class);
98          PurchaseOrderDocument po = null;
99  
100         if (StringUtils.isBlank(poDocNumber)) {
101             GlobalVariables.getMessageMap().putError(PurapPropertyConstants.PURCHASE_ORDER_DOCUMENT_NUMBER, PurapKeyConstants.ERROR_ELECTRONIC_INVOICE_GENERATION_PURCHASE_ORDER_NUMBER_EMPTY, new String[]{poDocNumber});
102             return mapping.findForward(RiceConstants.MAPPING_BASIC);
103         }
104         if (!getDocumentService().documentExists(poDocNumber)) {
105             GlobalVariables.getMessageMap().putError(PurapPropertyConstants.PURCHASE_ORDER_DOCUMENT_NUMBER, PurapKeyConstants.ERROR_ELECTRONIC_INVOICE_GENERATION_PURCHASE_ORDER_DOES_NOT_EXIST, poDocNumber);
106             return mapping.findForward(RiceConstants.MAPPING_BASIC);
107         }
108 
109         try {
110             po = poService.getPurchaseOrderByDocumentNumber(poDocNumber);
111         } catch (Exception e) {
112             throw e;
113         }
114 
115         response.setHeader("Cache-Control", "max-age=30");
116         response.setContentType("application/xml");
117         StringBuffer sbContentDispValue = new StringBuffer();
118         String useJavascript = request.getParameter("useJavascript");
119         if (useJavascript == null || useJavascript.equalsIgnoreCase("false")) {
120             sbContentDispValue.append("attachment");
121         } else {
122             sbContentDispValue.append("inline");
123         }
124         StringBuffer sbFilename = new StringBuffer();
125         sbFilename.append("PO_");
126         sbFilename.append(poDocNumber);
127         sbFilename.append(".xml");
128         sbContentDispValue.append("; filename=");
129         sbContentDispValue.append(sbFilename);
130         response.setHeader("Content-disposition", sbContentDispValue.toString());
131 
132         // lookup the PO and fill in the XML with valid data
133         if (po != null) {
134             String duns = "";
135             if (po.getVendorDetail() != null) {
136                 duns = StringUtils.defaultString(po.getVendorDetail().getVendorDunsNumber());
137             }
138 
139             DateTimeService dateTimeService = SpringContext.getBean(DateTimeService.class);
140             String currDate = ElectronicInvoiceUtils.getDateDisplayText(dateTimeService.getCurrentDate()); // getting date in ole format
141             String vendorNumber = po.getVendorDetail().getVendorNumber();
142 
143             String eInvoiceFile =
144                     "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
145                             "\n<!-- ******Testing tool generated XML****** Version 1.2." +
146                             "\n\n  Generated On " + currDate + " for PO " + po.getPurapDocumentIdentifier() + " (Doc# " + poDocNumber + ") -->\n\n" +
147                             "<!-- All the cXML attributes are junk values -->\n" +
148                             "<cXML payloadID=\"200807260401062080.964@eai002\"\n" +
149                             "    timestamp=\"2008-07-26T04:01:06-08:00\"\n" +
150                             "    version=\"1.2.014\" xml:lang=\"en\" \n" +
151                             "    xmlns=\"http://www.kuali.org/ole/purap/electronicInvoice\" \n" +
152                             "    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n" +
153                             "  <Header>\n" +
154                             "      <From>\n" +
155                             "          <Credential domain=\"DUNS\">\n" +
156                             "              <Identity>" + duns + "</Identity> <!-- DUNS number from PO Vendor " + vendorNumber + "-->\n" +
157                             "          </Credential>\n" +
158                             "      </From>\n" +
159                             "      <To>\n" +
160                             "          <Credential domain=\"NetworkId\">\n" +
161                             "              <Identity>" + "IU" + "</Identity> <!-- Hardcoded --> \n" +
162                             "          </Credential>\n" +
163                             "      </To>\n" +
164                             "      <Sender>\n" +
165                             "          <Credential domain=\"DUNS\">\n" +
166                             "              <Identity>" + duns + "</Identity> <!-- DUNS number from PO Vendor " + vendorNumber + "-->\n" +
167                             "          </Credential>\n" +
168                             "          <UserAgent/>\n" +
169                             "      </Sender>\n" +
170                             "  </Header>\n" +
171                             "  <Request deploymentMode=\"production\">\n" +
172                             "      <InvoiceDetailRequest>\n" +
173                             "          <InvoiceDetailRequestHeader\n" +
174                             "              invoiceDate=\"" + currDate + "\" invoiceID=\"" + RandomUtils.nextInt() + "\" operation=\"new\" purpose=\"standard\"> <!-- invoiceID=Random unique Id, invoiceDate=Curr date -->\n" +
175                             "              <InvoiceDetailHeaderIndicator/>\n" +
176                             "              <InvoiceDetailLineIndicator/>\n" +
177                             "              <InvoicePartner>\n" +
178                             getContactXMLChunk("billTo", po) +
179                             "              </InvoicePartner>\n" +
180                             "              <InvoicePartner>\n" +
181                             "                  <Contact addressID=\"" + RandomUtils.nextInt() + "\" role=\"remitTo\"> <!-- Vendor address -->\n" +
182                             "                      <Name xml:lang=\"en\">\n" +
183                             "                          " + po.getVendorName() + "\n" +
184                             "                      </Name>\n" +
185                             "                      <PostalAddress>\n" +
186                             "                          <Street>" + StringUtils.defaultString(po.getVendorLine1Address()) + "</Street>\n" +
187                             "                          <Street>" + StringUtils.defaultString(po.getVendorLine2Address()) + "</Street>\n" +
188                             "                          <City>" + StringUtils.defaultString(po.getVendorCityName()) + "</City>\n" +
189                             "                          <State>" + StringUtils.defaultString(po.getVendorStateCode()) + "</State>\n" +
190                             "                          <PostalCode>" + StringUtils.defaultString(po.getVendorPostalCode()) + "</PostalCode>\n" +
191                             "                          <Country isoCountryCode=\"" + StringUtils.defaultString(po.getVendorCountryCode()) + "\">\n" +
192                             "                              " + StringUtils.defaultString(po.getVendorCountry().getName()) + "\n" +
193                             "                          </Country>\n" +
194                             "                      </PostalAddress>\n" +
195                             "                  </Contact>\n" +
196                             "              </InvoicePartner>\n" +
197                             getDeliveryAddressXMLChunk("shipTo", po) +
198                             getPaymentTermXML(po) +
199                             "          </InvoiceDetailRequestHeader>\n" +
200                             "          <InvoiceDetailOrder>\n" +
201                             "              <InvoiceDetailOrderInfo>\n" +
202                             "                  <OrderReference\n" +
203                             "                      orderDate=\"" + ElectronicInvoiceUtils.getDateDisplayText(dateTimeService.getCurrentDate()) + "\" orderID=\"" + po.getPurapDocumentIdentifier() + "\"> <!--orderDate=Curr date,orderID=PO#-->\n" +
204                             "                      <DocumentReference payloadID=\"NA\" /> <!--HardCoded-->\n" +
205                             "                  </OrderReference>\n" +
206                             "              </InvoiceDetailOrderInfo>\n" +
207                             "              <!-- No junk values in Items-->\n";
208 
209             for (int i = 0; i < po.getItems().size(); i++) {
210                 List items = po.getItems();
211                 PurchaseOrderItem item = (PurchaseOrderItem) items.get(i);
212                 if (!item.getItemType().isAdditionalChargeIndicator()) {
213                     eInvoiceFile = eInvoiceFile + getPOItemXMLChunk(item);
214                 }
215             }
216 
217             KualiDecimal totalDollarAmt = po.getTotalDollarAmount() == null ? KualiDecimal.ZERO : po.getTotalDollarAmount();
218             eInvoiceFile = eInvoiceFile +
219 
220                     "          </InvoiceDetailOrder>\n" +
221                     "          <InvoiceDetailSummary>\n" +
222                     "              <SubtotalAmount>\n" +
223                     "                  <Money currency=\"USD\">" + po.getTotalPreTaxDollarAmount() + "</Money>\n" +
224                     "              </SubtotalAmount>\n" +
225                     "              <Tax>\n" +
226                     "                  <Money currency=\"USD\">" + po.getTotalTaxAmount() + "</Money>\n" +
227                     "                  <Description xml:lang=\"en\">Total Tax</Description>\n" +
228                     "              </Tax>\n" +
229                     "              <SpecialHandlingAmount>\n" +
230                     "                  <Money currency=\"USD\">0.00</Money>\n" +
231                     "              </SpecialHandlingAmount>\n" +
232                     "              <ShippingAmount>\n" +
233                     "                  <Money currency=\"USD\">0.00</Money>\n" +
234                     "              </ShippingAmount>\n" +
235                     "              <GrossAmount>\n" +
236                     "                  <Money currency=\"USD\">" + totalDollarAmt + "</Money>\n" +
237                     "              </GrossAmount>\n" +
238                     "              <InvoiceDetailDiscount>\n" +
239                     "                  <Money currency=\"USD\">0.00</Money>\n" +
240                     "                  </InvoiceDetailDiscount>\n" +
241                     "              <NetAmount>\n" +
242                     "                  <Money currency=\"USD\">" + totalDollarAmt + "</Money>\n" +
243                     "              </NetAmount>\n" +
244                     "              <DepositAmount>\n" +
245                     "                  <Money currency=\"USD\">0.00</Money>\n" +
246                     "              </DepositAmount>\n" +
247                     "              <DueAmount>\n" +
248                     "                  <Money currency=\"USD\">" + totalDollarAmt + "</Money>\n" +
249                     "              </DueAmount>\n" +
250                     "          </InvoiceDetailSummary>\n" +
251                     "      </InvoiceDetailRequest>\n" +
252                     "  </Request>\n" +
253                     "</cXML>";
254 
255             ServletOutputStream sos;
256             sos = response.getOutputStream();
257             ByteArrayOutputStream baOutStream = new ByteArrayOutputStream();
258             StringBufferInputStream inStream = new StringBufferInputStream(eInvoiceFile);
259             convert(baOutStream, inStream);
260             response.setContentLength(baOutStream.size());
261             baOutStream.writeTo(sos);
262             sos.flush();
263         }
264 
265         return mapping.findForward(OLEConstants.MAPPING_BASIC);
266     }
267 
268     private String getPaymentTermXML(PurchaseOrderDocument po) {
269         String returnXML = "";
270 
271         PaymentTermType paymentTerm = null;
272         if (po.getVendorDetail() != null) {
273             paymentTerm = po.getVendorDetail().getVendorPaymentTerms();
274         }
275 
276         if (paymentTerm != null) {
277             if (paymentTerm.getVendorNetDueNumber() != null) {
278                 returnXML =
279                         "              <InvoiceDetailPaymentTerm payInNumberOfDays=\"" + paymentTerm.getVendorNetDueNumber().toString() + "\" percentageRate=\"0\" />\n";
280             } else if (paymentTerm.getVendorPaymentTermsPercent() != null) {
281                 returnXML =
282                         "              <InvoiceDetailPaymentTerm payInNumberOfDays=\"0\" percentageRate=\"" + paymentTerm.getVendorPaymentTermsPercent() + "\" />\n";
283             }
284 
285         }
286 
287         return returnXML;
288     }
289 
290     private String getPOItemXMLChunk(PurchaseOrderItem item) {
291 
292         String itemUnitPrice = item.getItemUnitPrice() == null ?
293                 StringUtils.EMPTY :
294                 item.getItemUnitPrice().toString();
295 
296         String subTotal = StringUtils.EMPTY;
297         if (item.getItemUnitPrice() != null && item.getItemQuantity() != null) {
298             subTotal = (item.getItemUnitPrice().multiply(item.getItemQuantity().bigDecimalValue())).toString();
299         }
300 
301         return
302 
303                 "              <InvoiceDetailItem invoiceLineNumber=\"" + item.getItemLineNumber() + "\"\n" +
304                         "                  quantity=\"" + item.getItemQuantity() + "\">\n" +
305                         "                  <UnitOfMeasure>" + item.getItemUnitOfMeasureCode() + "</UnitOfMeasure>\n" +
306                         "                  <UnitPrice>\n" +
307                         "                      <Money currency=\"USD\">" + itemUnitPrice + "</Money>\n" +
308                         "                  </UnitPrice>\n" +
309                         "                  <InvoiceDetailItemReference lineNumber=\"" + item.getItemLineNumber() + "\">\n" +
310                         "                      <ItemID>\n" +
311                         "                          <SupplierPartID>" + StringUtils.defaultString(item.getItemCatalogNumber()) + "</SupplierPartID>\n" +
312                         "                      </ItemID>\n" +
313                         "                      <Description xml:lang=\"en\">" + StringUtils.defaultString(item.getItemDescription()) + "</Description>\n" +
314                         "                  </InvoiceDetailItemReference>\n" +
315                         "                  <SubtotalAmount>\n" +
316                         "                      <Money currency=\"USD\" >" + subTotal + "</Money>\n" +
317                         "                  </SubtotalAmount>\n" +
318                         "              </InvoiceDetailItem>\n";
319 
320     }
321 
322     private String getDeliveryAddressXMLChunk(String addressType,
323                                               PurchaseOrderDocument po) {
324 
325         String deliveryDate = "";
326         if (po.getDeliveryRequiredDate() != null) {
327             deliveryDate = ElectronicInvoiceUtils.getDateDisplayText(po.getDeliveryRequiredDate());
328         }
329 
330         String returnXML = "";
331 
332         if (StringUtils.isNotEmpty(deliveryDate)) {
333             returnXML += "              <InvoiceDetailShipping shippingDate=\"" + deliveryDate + "\"> <!--Delivery reqd date -->\n";
334         } else {
335             returnXML += "              <InvoiceDetailShipping> <!-- shipTo address same as billTo-->\n";
336         }
337         returnXML += getContactXMLChunk("shipTo", po) + "              </InvoiceDetailShipping>\n";
338 
339         return returnXML;
340 
341     }
342 
343     private String getContactXMLChunk(String addressType,
344                                       PurchaseOrderDocument po) {
345 
346         String returnXML =
347 
348                 "                  <Contact addressID=\"" + RandomUtils.nextInt() + "\" role=\"" + addressType + "\"> <!-- addressId=Random Unique Id -->\n" +
349                         "                      <Name xml:lang=\"en\">" + po.getDeliveryCampusCode() + " - " + po.getDeliveryBuildingName() + "</Name> <!-- Format:CampusCode - Bldg Nm -->\n" +
350                         "                      <PostalAddress>\n" +
351                         "                          <Street>" + StringUtils.defaultString(po.getDeliveryBuildingLine1Address()) + "</Street>\n" +
352                         "                          <Street>" + StringUtils.defaultString(po.getDeliveryBuildingLine2Address()) + "</Street>\n" +
353                         "                          <City>" + StringUtils.defaultString(po.getDeliveryCityName()) + "</City>\n" +
354                         "                          <State>" + StringUtils.defaultString(po.getDeliveryStateCode()) + "</State>\n" +
355                         "                          <PostalCode>" + StringUtils.defaultString(po.getDeliveryPostalCode()) + "</PostalCode>\n" +
356                         "                          <Country isoCountryCode=\"" + StringUtils.defaultString(po.getDeliveryCountryCode()) + "\">\n" +
357                         "                              " + StringUtils.defaultString(po.getDeliveryCountryName()) + "\n" +
358                         "                          </Country>\n" +
359                         "                      </PostalAddress>\n";
360 
361         if (StringUtils.isNotEmpty(po.getDeliveryToEmailAddress())) {
362             returnXML += "                      <Email name=\"" + po.getDeliveryToEmailAddress() + "\">" + po.getDeliveryToEmailAddress() + "</Email>\n";
363         }
364 
365         if (StringUtils.isNotEmpty(po.getDeliveryToPhoneNumber())) {
366             returnXML +=
367                     "                      <Phone name=\"" + po.getDeliveryToPhoneNumber() + "\">\n" +
368                             "                          <TelephoneNumber>\n" +
369                             "                              <CountryCode isoCountryCode=\"US\">1</CountryCode>\n" +
370                             "                              <AreaOrCityCode>" + getPhoneNumber(AREA_C0DE, po.getDeliveryToPhoneNumber()) + "</AreaOrCityCode>\n" +
371                             "                              <Number>" + getPhoneNumber(PHONE_NUMBER, po.getDeliveryToPhoneNumber()) + "</Number>\n" +
372                             "                          </TelephoneNumber>\n" +
373                             "                      </Phone>\n";
374         }
375 
376         returnXML += "                  </Contact>\n";
377         return returnXML;
378     }
379 
380     private String getPhoneNumber(String whichPart, String phNo) {
381 
382         if (StringUtils.isEmpty(phNo)) {
383             return StringUtils.EMPTY;
384         }
385 
386         if (StringUtils.equals(whichPart, AREA_C0DE)) {
387             return phNo.substring(0, 3);
388         } else if (StringUtils.equals(whichPart, PHONE_NUMBER)) {
389             return phNo.substring(3, phNo.length());
390         }
391 
392         return StringUtils.EMPTY;
393     }
394 
395     private boolean convert(java.io.OutputStream out, java.io.InputStream in) {
396         try {
397             int r;
398             while ((r = in.read()) != -1) {
399                 out.write(r);
400             }
401             return true;
402         } catch (java.io.IOException ioe) {
403             return false;
404         }
405     }
406 
407     /**
408      * @return the default implementation of the KRAD DocumentService
409      */
410     protected DocumentService getDocumentService() {
411         if (documentService == null) {
412             documentService = KRADServiceLocatorWeb.getDocumentService();
413         }
414         return documentService;
415     }
416 }