001    /*
002     * Copyright 2011 The Kuali Foundation.
003     * 
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     * http://www.opensource.org/licenses/ecl2.php
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.kfs.module.purap.document.web.struts;
017    
018    import java.io.ByteArrayInputStream;
019    import java.io.ByteArrayOutputStream;
020    import java.io.InputStream;
021    import java.io.StringBufferInputStream;
022    
023    import javax.servlet.ServletOutputStream;
024    import javax.servlet.http.HttpServletRequest;
025    import javax.servlet.http.HttpServletResponse;
026    
027    import org.apache.commons.io.IOUtils;
028    import org.apache.commons.lang.StringUtils;
029    import org.apache.commons.lang.math.RandomUtils;
030    import org.apache.struts.action.ActionForm;
031    import org.apache.struts.action.ActionForward;
032    import org.apache.struts.action.ActionMapping;
033    import org.apache.struts.upload.FormFile;
034    import org.kuali.kfs.module.purap.batch.ElectronicInvoiceInputFileType;
035    import org.kuali.kfs.module.purap.businessobject.ElectronicInvoice;
036    import org.kuali.kfs.module.purap.businessobject.PurchaseOrderItem;
037    import org.kuali.kfs.module.purap.document.PurchaseOrderDocument;
038    import org.kuali.kfs.module.purap.document.service.PurchaseOrderService;
039    import org.kuali.kfs.module.purap.util.ElectronicInvoiceUtils;
040    import org.kuali.kfs.sys.KFSConstants;
041    import org.kuali.kfs.sys.batch.service.BatchInputFileService;
042    import org.kuali.kfs.sys.context.SpringContext;
043    import org.kuali.kfs.vnd.businessobject.PaymentTermType;
044    import org.kuali.rice.kns.exception.AuthorizationException;
045    import org.kuali.rice.kns.service.DateTimeService;
046    import org.kuali.rice.kns.service.KualiConfigurationService;
047    import org.kuali.rice.kns.util.GlobalVariables;
048    import org.kuali.rice.kns.util.KualiDecimal;
049    import org.kuali.rice.kns.web.struts.action.KualiAction;
050    
051    /**
052     * Struts Action for printing Purap documents outside of a document action
053     */
054    public class ElectronicInvoiceTestAction extends KualiAction {
055        private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(ElectronicInvoiceTestAction.class);
056        
057        private static final String AREA_C0DE = "areaCode";
058        private static final String PHONE_NUMBER = "phoneNumber";
059    
060        /**
061         * @see org.kuali.rice.kns.web.struts.action.KualiAction#checkAuthorization(org.apache.struts.action.ActionForm, java.lang.String)
062         * 
063         * Only allow users to test eInvoicing in the test environment
064         */
065        @Override
066        protected void checkAuthorization(ActionForm form, String methodToCall) throws AuthorizationException {
067            if (SpringContext.getBean(KualiConfigurationService.class).isProductionEnvironment()) {
068                //this process is not available for production
069                throw new AuthorizationException(GlobalVariables.getUserSession().getPerson().getPrincipalName(), methodToCall, this.getClass().getSimpleName());
070            }
071        }
072    
073        @Override
074        public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
075            DateTimeService dateTimeService = SpringContext.getBean(DateTimeService.class);
076            
077            checkAuthorization(form, "");
078            
079            //get parameters - are we doing upload xml or create based on PO?
080            String action = request.getParameter("action");
081            
082            String currDate = ElectronicInvoiceUtils.getDateDisplayText(dateTimeService.getCurrentDate()); // getting date in kfs format
083            
084            if ("postXML".equalsIgnoreCase(action)) {
085                // get the file and send the contents to the eInvoice mechanism and display the results
086                ElectronicInvoiceTestForm rejectForm = (ElectronicInvoiceTestForm) form;
087                FormFile xmlFile = rejectForm.getXmlFile();
088                if (xmlFile != null) {
089                    if (!StringUtils.isEmpty(xmlFile.getFileName())) {
090                        if (xmlFile.getFileName().endsWith(".xml")) {
091                            
092                            BatchInputFileService batchInputFileService = SpringContext.getBean(BatchInputFileService.class);
093                            ElectronicInvoiceInputFileType batchType = SpringContext.getBean(ElectronicInvoiceInputFileType.class);
094                            
095                            byte[] fileByteContent = IOUtils.toByteArray(xmlFile.getInputStream());
096    
097                            Object parsedObject = batchInputFileService.parse(batchType, fileByteContent);
098                            ElectronicInvoice eInvoice = (ElectronicInvoice)parsedObject;
099                            eInvoice.setFileName(xmlFile.getFileName());
100                            
101                            if (parsedObject != null) {
102                                boolean validateSuccessful = batchInputFileService.validate(batchType, parsedObject);
103    
104                                if (validateSuccessful) {
105                                    InputStream saveStream = new ByteArrayInputStream(fileByteContent);
106                                    batchInputFileService.save(GlobalVariables.getUserSession().getPerson(), batchType, ""+RandomUtils.nextInt(), saveStream, parsedObject);
107                                }
108                            }
109                        } else {
110                            throw new RuntimeException("Invalid file type " + xmlFile.getFileName());
111                        }
112                    } else {
113                        throw new RuntimeException("Invalid file name " + xmlFile.getFileName());
114                    }
115                } else {
116                    throw new RuntimeException("Error getting xml file");
117                }
118            } else if ("returnXML".equalsIgnoreCase(action)) {
119                
120                String poDocNumber = request.getParameter("poDocNumber");
121                
122                LOG.info("Generating xml for the po - " + poDocNumber);
123                
124                PurchaseOrderService poService = SpringContext.getBean(PurchaseOrderService.class);
125                PurchaseOrderDocument po = null;
126                try{
127                    po = poService.getPurchaseOrderByDocumentNumber(poDocNumber);
128                }catch(Exception e){
129                    throw e;
130                }
131                
132                response.setHeader("Cache-Control", "max-age=30");
133                response.setContentType("application/xml");
134    
135                StringBuffer sbContentDispValue = new StringBuffer();
136                String useJavascript = request.getParameter("useJavascript");
137                if (useJavascript == null || useJavascript.equalsIgnoreCase("false")) {
138                    sbContentDispValue.append("attachment");
139                }
140                else {
141                    sbContentDispValue.append("inline");
142                }
143                StringBuffer sbFilename = new StringBuffer();
144                sbFilename.append("PO_");
145                sbFilename.append(poDocNumber);
146                sbFilename.append(".xml");
147                sbContentDispValue.append("; filename=");
148                sbContentDispValue.append(sbFilename);
149    
150                response.setHeader("Content-disposition", sbContentDispValue.toString());
151    
152                
153                // lookup the PO and fill in the XML will valid data
154                
155                if (po != null) {   
156                    
157                    String duns = "";
158                    if (po.getVendorDetail() != null){
159                        duns = StringUtils.defaultString(po.getVendorDetail().getVendorDunsNumber());
160                    }
161                    
162                    String vendorNumber = po.getVendorDetail().getVendorNumber();
163                    
164                    String eInvoiceFile = 
165                    
166                    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
167                    "\n<!-- ******Testing tool generated XML****** Version 1.2." +
168                    "\n\n  Generated On " + currDate + " for PO " + po.getPurapDocumentIdentifier() + " (Doc# " + poDocNumber + ") -->\n\n" +   
169                    "<!-- All the cXML attributes are junk values -->\n" +
170                    "<cXML payloadID=\"200807260401062080.964@eai002\"\n" +
171                    "    timestamp=\"2008-07-26T04:01:06-08:00\"\n" +
172                    "    version=\"1.2.014\" xml:lang=\"en\" \n" +
173                    "    xmlns=\"http://www.kuali.org/kfs/purap/electronicInvoice\" \n" +
174                    "    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n" +
175                    "  <Header>\n" +
176                    "      <From>\n" +
177                    "          <Credential domain=\"DUNS\">\n" +
178                    "              <Identity>" + duns + "</Identity> <!-- DUNS number from PO Vendor " + vendorNumber + "-->\n" +
179                    "          </Credential>\n" +
180                    "      </From>\n" +
181                    "      <To>\n" +
182                    "          <Credential domain=\"NetworkId\">\n" +
183                    "              <Identity>" + "IU" + "</Identity> <!-- Hardcoded --> \n" +
184                    "          </Credential>\n" +
185                    "      </To>\n" +
186                    "      <Sender>\n" +
187                    "          <Credential domain=\"DUNS\">\n" +
188                    "              <Identity>" + duns + "</Identity> <!-- DUNS number from PO Vendor " + vendorNumber + "-->\n" +
189                    "          </Credential>\n" +
190                    "          <UserAgent/>\n" +
191                    "      </Sender>\n" +
192                    "  </Header>\n" +
193                    "  <Request deploymentMode=\"production\">\n" +
194                    "      <InvoiceDetailRequest>\n" +
195                    "          <InvoiceDetailRequestHeader\n" +
196                    "              invoiceDate=\"" + currDate + "\" invoiceID=\"" + RandomUtils.nextInt() + "\" operation=\"new\" purpose=\"standard\"> <!-- invoiceID=Random unique Id, invoiceDate=Curr date -->\n" +
197                    "              <InvoiceDetailHeaderIndicator/>\n" +
198                    "              <InvoiceDetailLineIndicator/>\n" +
199                    "              <InvoicePartner>\n" +
200                                   getContactXMLChunk("billTo", po) +
201                    "              </InvoicePartner>\n" +
202                    "              <InvoicePartner>\n" +
203                    "                  <Contact addressID=\"" + RandomUtils.nextInt() + "\" role=\"remitTo\"> <!-- Vendor address -->\n" + 
204                    "                      <Name xml:lang=\"en\">\n" +
205                    "                          " + po.getVendorName() + "\n" +
206                    "                      </Name>\n" +
207                    "                      <PostalAddress>\n" +
208                    "                          <Street>" + StringUtils.defaultString(po.getVendorLine1Address()) + "</Street>\n" +
209                    "                          <Street>" + StringUtils.defaultString(po.getVendorLine2Address()) + "</Street>\n" +
210                    "                          <City>" + StringUtils.defaultString(po.getVendorCityName()) + "</City>\n" +
211                    "                          <State>" + StringUtils.defaultString(po.getVendorStateCode()) + "</State>\n" +
212                    "                          <PostalCode>" + StringUtils.defaultString(po.getVendorPostalCode()) + "</PostalCode>\n" +
213                    "                          <Country isoCountryCode=\"" + StringUtils.defaultString(po.getVendorCountryCode()) + "\">\n" +
214                    "                              " + StringUtils.defaultString(po.getVendorCountry().getPostalCountryName()) + "\n" +
215                    "                          </Country>\n" +
216                    "                      </PostalAddress>\n" +
217                    "                  </Contact>\n" +
218                    "              </InvoicePartner>\n" +
219                                    getDeliveryAddressXMLChunk("shipTo",po) +
220                                    getPaymentTermXML(po) +
221                    "          </InvoiceDetailRequestHeader>\n" +
222                    "          <InvoiceDetailOrder>\n" +
223                    "              <InvoiceDetailOrderInfo>\n" +
224                    "                  <OrderReference\n" +
225                    "                      orderDate=\"" + ElectronicInvoiceUtils.getDateDisplayText(dateTimeService.getCurrentDate()) + "\" orderID=\"" + po.getPurapDocumentIdentifier() + "\"> <!--orderDate=Curr date,orderID=PO#-->\n" +
226                    "                      <DocumentReference payloadID=\"NA\" /> <!--HardCoded-->\n" +
227                    "                  </OrderReference>\n" +
228                    "              </InvoiceDetailOrderInfo>\n" +
229                    "              <!-- No junk values in Items-->\n";
230                    
231                                   for (int i = 0; i < po.getItems().size(); i++) {
232                                       PurchaseOrderItem item = (PurchaseOrderItem)po.getItem(i);
233                                       if (!item.getItemType().isAdditionalChargeIndicator()){
234                                           eInvoiceFile = eInvoiceFile + getPOItemXMLChunk((PurchaseOrderItem)po.getItem(i));
235                                       }
236                                   }
237                    
238                    KualiDecimal totalDollarAmt = po.getTotalDollarAmount() == null ? KualiDecimal.ZERO : po.getTotalDollarAmount();
239                    eInvoiceFile = eInvoiceFile +
240                    
241                    "          </InvoiceDetailOrder>\n" +
242                    "          <InvoiceDetailSummary>\n" +
243                    "              <SubtotalAmount>\n" +
244                    "                  <Money currency=\"USD\">" + po.getTotalPreTaxDollarAmount() + "</Money>\n" +
245                    "              </SubtotalAmount>\n" +
246                    "              <Tax>\n" +
247                    "                  <Money currency=\"USD\">" + po.getTotalTaxAmount() + "</Money>\n" +
248                    "                  <Description xml:lang=\"en\">Total Tax</Description>\n" +
249                    "              </Tax>\n" +
250                    "              <SpecialHandlingAmount>\n" +
251                    "                  <Money currency=\"USD\">0.00</Money>\n" +
252                    "              </SpecialHandlingAmount>\n" +
253                    "              <ShippingAmount>\n" +
254                    "                  <Money currency=\"USD\">0.00</Money>\n" +
255                    "              </ShippingAmount>\n" +
256                    "              <GrossAmount>\n" +
257                    "                  <Money currency=\"USD\">" + totalDollarAmt + "</Money>\n" +
258                    "              </GrossAmount>\n" +
259                    "              <InvoiceDetailDiscount>\n" +
260                    "                  <Money currency=\"USD\">0.00</Money>\n" +
261                    "                  </InvoiceDetailDiscount>\n" +
262                    "              <NetAmount>\n" +
263                    "                  <Money currency=\"USD\">" + totalDollarAmt + "</Money>\n" +
264                    "              </NetAmount>\n" +
265                    "              <DepositAmount>\n" +
266                    "                  <Money currency=\"USD\">0.00</Money>\n" +
267                    "              </DepositAmount>\n" +
268                    "              <DueAmount>\n" +
269                    "                  <Money currency=\"USD\">" + totalDollarAmt + "</Money>\n" +
270                    "              </DueAmount>\n" +
271                    "          </InvoiceDetailSummary>\n" +
272                    "      </InvoiceDetailRequest>\n" +
273                    "  </Request>\n" +
274                    "</cXML>";
275    
276    //                response.setContentLength(eInvoiceFile.length());
277    
278                    ServletOutputStream sos;
279    
280                    sos = response.getOutputStream();
281                    
282                    ByteArrayOutputStream baOutStream = new ByteArrayOutputStream();
283                    StringBufferInputStream inStream = new StringBufferInputStream(eInvoiceFile);
284                    convert(baOutStream, inStream);
285    //                
286    //                baOutStream.flush();
287                    response.setContentLength(baOutStream.size());
288    
289    //                ServletOutputStream sosTemp = response.getOutputStream();
290                    baOutStream.writeTo(sos);
291                    sos.flush();
292                    
293                    return null;
294                }
295            }
296    
297          return mapping.findForward(KFSConstants.MAPPING_BASIC);
298        }
299    
300        private String getPaymentTermXML(PurchaseOrderDocument po){
301            String returnXML = "";
302            
303            PaymentTermType paymentTerm = null;
304            if (po.getVendorDetail() != null){
305                paymentTerm = po.getVendorDetail().getVendorPaymentTerms();
306            }
307            
308            if (paymentTerm != null){
309                if (paymentTerm.getVendorNetDueNumber() != null){
310                    returnXML = 
311                    "              <InvoiceDetailPaymentTerm payInNumberOfDays=\"" + paymentTerm.getVendorNetDueNumber().toString() + "\" percentageRate=\"0\" />\n";     
312                }else if (paymentTerm.getVendorPaymentTermsPercent() != null){
313                    returnXML = 
314                    "              <InvoiceDetailPaymentTerm payInNumberOfDays=\"0\" percentageRate=\"" + paymentTerm.getVendorPaymentTermsPercent() + "\" />\n";
315                }
316                
317            }
318            
319            return returnXML;
320        }
321        
322        private String getPOItemXMLChunk(PurchaseOrderItem item){
323            
324            String itemUnitPrice = item.getItemUnitPrice() == null ?
325                                   StringUtils.EMPTY :
326                                   item.getItemUnitPrice().toString();
327            
328            String subTotal = StringUtils.EMPTY;
329            if (item.getItemUnitPrice() != null && item.getItemQuantity() != null){
330                subTotal = (item.getItemUnitPrice().multiply(item.getItemQuantity().bigDecimalValue())).toString();    
331            }
332            
333            return 
334            
335            "              <InvoiceDetailItem invoiceLineNumber=\"" + item.getItemLineNumber() + "\"\n" +
336            "                  quantity=\"" + item.getItemQuantity() + "\">\n" +
337            "                  <UnitOfMeasure>" + item.getItemUnitOfMeasureCode() + "</UnitOfMeasure>\n" +
338            "                  <UnitPrice>\n" +
339            "                      <Money currency=\"USD\">" + itemUnitPrice + "</Money>\n" +
340            "                  </UnitPrice>\n" +
341            "                  <InvoiceDetailItemReference lineNumber=\"" + item.getItemLineNumber() + "\">\n" +
342            "                      <ItemID>\n" +
343            "                          <SupplierPartID>" + StringUtils.defaultString(item.getItemCatalogNumber()) + "</SupplierPartID>\n" +
344            "                      </ItemID>\n" +
345            "                      <Description xml:lang=\"en\">" + StringUtils.defaultString(item.getItemDescription()) + "</Description>\n" +
346            "                  </InvoiceDetailItemReference>\n" +
347            "                  <SubtotalAmount>\n" +
348            "                      <Money currency=\"USD\" >" + subTotal + "</Money>\n" +
349            "                  </SubtotalAmount>\n" +
350            "              </InvoiceDetailItem>\n";
351            
352        }
353        
354        private String getDeliveryAddressXMLChunk(String addressType,
355                                                  PurchaseOrderDocument po){
356            
357            String deliveryDate = "";
358            if (po.getDeliveryRequiredDate() != null){
359                deliveryDate = ElectronicInvoiceUtils.getDateDisplayText(po.getDeliveryRequiredDate());
360            }
361            
362            String returnXML = "";
363            
364            if (StringUtils.isNotEmpty(deliveryDate)){
365                returnXML = returnXML + "              <InvoiceDetailShipping shippingDate=\"" +  deliveryDate + "\"> <!--Delivery reqd date -->\n";
366            }else{
367                returnXML = returnXML + "              <InvoiceDetailShipping> <!-- shipTo address same as billTo-->\n";
368            }
369    
370            returnXML = returnXML + 
371                        getContactXMLChunk("shipTo",po) +
372                        "              </InvoiceDetailShipping>\n";
373            
374            return returnXML;
375            
376        }
377        
378        private String getContactXMLChunk(String addressType,
379                                          PurchaseOrderDocument po){
380            
381            String returnXML =          
382            
383            "                  <Contact addressID=\"" + RandomUtils.nextInt() + "\" role=\"" + addressType + "\"> <!-- addressId=Random Unique Id -->\n" +
384            "                      <Name xml:lang=\"en\">" + po.getDeliveryCampusCode() + " - " + po.getDeliveryBuildingName() + "</Name> <!-- Format:CampusCode - Bldg Nm -->\n" +
385            "                      <PostalAddress>\n" +
386            "                          <Street>" + StringUtils.defaultString(po.getDeliveryBuildingLine1Address()) + "</Street>\n" +
387            "                          <Street>" + StringUtils.defaultString(po.getDeliveryBuildingLine2Address()) + "</Street>\n" +
388            "                          <City>" + StringUtils.defaultString(po.getDeliveryCityName()) + "</City>\n" +
389            "                          <State>" + StringUtils.defaultString(po.getDeliveryStateCode()) + "</State>\n" +
390            "                          <PostalCode>" + StringUtils.defaultString(po.getDeliveryPostalCode()) + "</PostalCode>\n" +
391            "                          <Country isoCountryCode=\"" + StringUtils.defaultString(po.getDeliveryCountryCode()) + "\">\n" +
392            "                              " + StringUtils.defaultString(po.getDeliveryCountryName()) + "\n" +
393            "                          </Country>\n" +
394            "                      </PostalAddress>\n";
395            
396            if (StringUtils.isNotEmpty(po.getDeliveryToEmailAddress())){
397                returnXML = returnXML + 
398                "                      <Email name=\"" + po.getDeliveryToEmailAddress() + "\">" + po.getDeliveryToEmailAddress() + "</Email>\n";
399            }
400            
401            if (StringUtils.isNotEmpty(po.getDeliveryToPhoneNumber())){
402                returnXML = returnXML + 
403                "                      <Phone name=\"" + po.getDeliveryToPhoneNumber() + "\">\n" +
404                "                          <TelephoneNumber>\n" +
405                "                              <CountryCode isoCountryCode=\"US\">1</CountryCode>\n" +
406                "                              <AreaOrCityCode>" + getPhoneNumber(AREA_C0DE, po.getDeliveryToPhoneNumber()) + "</AreaOrCityCode>\n" +
407                "                              <Number>" + getPhoneNumber(PHONE_NUMBER, po.getDeliveryToPhoneNumber()) + "</Number>\n" +
408                "                          </TelephoneNumber>\n" +
409                "                      </Phone>\n";
410            }    
411            
412            returnXML = returnXML + 
413    //        "                      <URL name=\"sampleCompanyURL\">www.abc.com</URL>\n" +
414            "                  </Contact>\n";
415            
416            
417            return returnXML;
418            
419        }
420        
421        private String getPhoneNumber(String whichPart,String phNo){
422    
423            if (StringUtils.isEmpty(phNo)){
424                return StringUtils.EMPTY;
425            }
426            
427            if (StringUtils.equals(whichPart,AREA_C0DE)){
428                return phNo.substring(0,3);
429            }else if (StringUtils.equals(whichPart,PHONE_NUMBER)){
430                return phNo.substring(3,phNo.length());
431            }
432            
433            return StringUtils.EMPTY;
434        }
435        
436        private boolean convert(java.io.OutputStream out, java.io.InputStream in) {
437            try {
438                int r;
439                while ((r=in.read())!=-1) {
440                    out.write(r);
441                }
442                return true;
443            }catch (java.io.IOException ioe) {
444                return false;
445            }
446        }
447        
448    }
449