View Javadoc
1   /*
2    * The Kuali Financial System, a comprehensive financial management system for higher education.
3    * 
4    * Copyright 2005-2014 The Kuali Foundation
5    * 
6    * This program is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU Affero General Public License as
8    * published by the Free Software Foundation, either version 3 of the
9    * License, or (at your option) any later version.
10   * 
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU Affero General Public License for more details.
15   * 
16   * You should have received a copy of the GNU Affero General Public License
17   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  package org.kuali.kfs.module.purap.document.service.impl;
20  
21  import java.text.SimpleDateFormat;
22  import java.util.Date;
23  import java.util.Iterator;
24  import java.util.List;
25  
26  import org.apache.commons.lang.StringUtils;
27  import org.kuali.kfs.module.purap.PurapConstants;
28  import org.kuali.kfs.module.purap.businessobject.PurchaseOrderItem;
29  import org.kuali.kfs.module.purap.dataaccess.B2BDao;
30  import org.kuali.kfs.module.purap.document.PurchaseOrderDocument;
31  import org.kuali.kfs.module.purap.document.RequisitionDocument;
32  import org.kuali.kfs.module.purap.document.service.B2BPurchaseOrderService;
33  import org.kuali.kfs.module.purap.document.service.RequisitionService;
34  import org.kuali.kfs.module.purap.exception.B2BConnectionException;
35  import org.kuali.kfs.module.purap.exception.CxmlParseError;
36  import org.kuali.kfs.module.purap.util.PurApDateFormatUtils;
37  import org.kuali.kfs.module.purap.util.cxml.B2BParserHelper;
38  import org.kuali.kfs.module.purap.util.cxml.PurchaseOrderResponse;
39  import org.kuali.kfs.sys.context.SpringContext;
40  import org.kuali.kfs.vnd.businessobject.ContractManager;
41  import org.kuali.rice.core.api.datetime.DateTimeService;
42  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
43  import org.kuali.rice.kew.api.WorkflowDocument;
44  import org.kuali.rice.kim.api.identity.Person;
45  import org.kuali.rice.kim.api.identity.PersonService;
46  import org.kuali.rice.kim.api.identity.principal.Principal;
47  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
48  import org.kuali.rice.krad.util.ObjectUtils;
49  import org.springframework.transaction.annotation.Transactional;
50  
51  @Transactional
52  public class B2BPurchaseOrderSciquestServiceImpl implements B2BPurchaseOrderService {
53      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(B2BPurchaseOrderSciquestServiceImpl.class);
54  
55      protected B2BDao b2bDao;
56      protected RequisitionService requisitionService;
57      protected ParameterService parameterService;
58      protected PersonService personService;
59  
60      // injected values
61      private String b2bEnvironment;
62      private String b2bUserAgent;
63      private String b2bPurchaseOrderURL;
64      private String b2bPurchaseOrderIdentity;
65      private String b2bPurchaseOrderPassword;
66  
67      /**
68       * @see org.kuali.kfs.module.purap.document.service.B2BPurchaseOrderService#sendPurchaseOrder(org.kuali.kfs.module.purap.document.PurchaseOrderDocument)
69       */
70      public String sendPurchaseOrder(PurchaseOrderDocument purchaseOrder) {
71          /*
72           * IMPORTANT DESIGN NOTE: We need the contract manager's name, phone number, and e-mail address. B2B orders that don't
73           * qualify to become APO's will have contract managers on the PO, and the ones that DO become APO's will not. We decided to
74           * always get the contract manager from the B2B contract associated with the order, and for B2B orders to ignore the
75           * contract manager field on the PO. We pull the name and phone number from the contract manager table and get the e-mail
76           * address from the user data.
77           */
78  
79          ContractManager contractManager = purchaseOrder.getVendorContract().getContractManager();
80          String contractManagerEmail = getContractManagerEmail(contractManager);
81  
82          String vendorDuns = purchaseOrder.getVendorDetail().getVendorDunsNumber();
83  
84          RequisitionDocument r = requisitionService.getRequisitionById(purchaseOrder.getRequisitionIdentifier());
85          WorkflowDocument reqWorkflowDoc = r.getDocumentHeader().getWorkflowDocument();
86          String requisitionInitiatorPrincipalId = getRequisitionInitiatorPrincipal(reqWorkflowDoc.getInitiatorPrincipalId());
87  
88          if (LOG.isDebugEnabled()) {
89              LOG.debug("sendPurchaseOrder(): b2bPurchaseOrderURL is " + b2bPurchaseOrderURL);
90          }
91  
92          String validateErrors = verifyCxmlPOData(purchaseOrder, requisitionInitiatorPrincipalId, b2bPurchaseOrderPassword, contractManager, contractManagerEmail, vendorDuns);
93          if (!StringUtils.isEmpty(validateErrors)) {
94              return validateErrors;
95          }
96  
97          StringBuffer transmitErrors = new StringBuffer();
98  
99          try {
100             LOG.debug("sendPurchaseOrder() Generating cxml");
101             String cxml = getCxml(purchaseOrder, requisitionInitiatorPrincipalId, b2bPurchaseOrderPassword, contractManager, contractManagerEmail, vendorDuns);
102 
103             LOG.info("sendPurchaseOrder() Sending cxml\n" + cxml);
104             String responseCxml = b2bDao.sendPunchOutRequest(cxml, b2bPurchaseOrderURL);
105 
106             LOG.info("sendPurchaseOrder(): Response cXML for po #" + purchaseOrder.getPurapDocumentIdentifier() + ":\n" + responseCxml);
107 
108             PurchaseOrderResponse poResponse = B2BParserHelper.getInstance().parsePurchaseOrderResponse(responseCxml);
109             String statusText = poResponse.getStatusText();
110             if (LOG.isDebugEnabled()) {
111                 LOG.debug("sendPurchaseOrder(): statusText is " + statusText);
112             }
113             if (ObjectUtils.isNull(statusText) || (!"success".equalsIgnoreCase(statusText.trim()))) {
114                 LOG.error("sendPurchaseOrder(): PO cXML for po number " + purchaseOrder.getPurapDocumentIdentifier() + " failed sending to SciQuest:\n" + statusText);
115                 transmitErrors.append("Unable to send Purchase Order: " + statusText);
116 
117                 // find any additional error messages that might have been sent
118                 List errorMessages = poResponse.getPOResponseErrorMessages();
119                 if (ObjectUtils.isNotNull(errorMessages) && !errorMessages.isEmpty()) {
120                     for (Iterator iter = errorMessages.iterator(); iter.hasNext();) {
121                         String errorMessage = (String) iter.next();
122                         if (ObjectUtils.isNotNull(errorMessage)) {
123                             LOG.error("sendPurchaseOrder(): SciQuest error message for po number " + purchaseOrder.getPurapDocumentIdentifier() + ": " + errorMessage);
124                             transmitErrors.append("Error sending Purchase Order: " + errorMessage);
125                         }
126                     }
127                 }
128             }
129         }
130         catch (B2BConnectionException e) {
131             LOG.error("sendPurchaseOrder() Error connecting to b2b", e);
132             transmitErrors.append("Connection to Sciquest failed.");
133         }
134         catch (CxmlParseError e) {
135             LOG.error("sendPurchaseOrder() Error Parsing", e);
136             transmitErrors.append("Unable to read cxml returned from Sciquest.");
137         }
138         catch (Throwable e) {
139             LOG.error("sendPurchaseOrder() Unknown Error", e);
140             transmitErrors.append("Unexpected error occurred while attempting to transmit Purchase Order.");
141         }
142 
143         return transmitErrors.toString();
144     }
145 
146     /**
147      * @see org.kuali.kfs.module.purap.document.service.B2BPurchaseOrderService#getCxml(org.kuali.kfs.module.purap.document.PurchaseOrderDocument,
148      *      org.kuali.rice.kim.api.identity.Person, java.lang.String, org.kuali.kfs.vnd.businessobject.ContractManager,
149      *      java.lang.String, java.lang.String)
150      */
151     public String getCxml(PurchaseOrderDocument purchaseOrder, String requisitionInitiatorPrincipalId, String password, ContractManager contractManager, String contractManagerEmail, String vendorDuns) {
152 
153         StringBuffer cxml = new StringBuffer();
154 
155         cxml.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
156         cxml.append("<!DOCTYPE PurchaseOrderMessage SYSTEM \"PO.dtd\">\n");
157         cxml.append("<PurchaseOrderMessage version=\"2.0\">\n");
158         cxml.append("  <Header>\n");
159 
160         // MessageId - can be whatever you would like it to be. Just make it unique.
161         cxml.append("    <MessageId>KFS_cXML_PO</MessageId>\n");
162 
163         // Timestamp - it doesn't matter what's in the timezone, just that it's there (need "T" space between date/time)
164         Date d = SpringContext.getBean(DateTimeService.class).getCurrentDate();
165         SimpleDateFormat date = PurApDateFormatUtils.getSimpleDateFormat(PurapConstants.NamedDateFormats.CXML_SIMPLE_DATE_FORMAT);
166         SimpleDateFormat time = PurApDateFormatUtils.getSimpleDateFormat(PurapConstants.NamedDateFormats.CXML_SIMPLE_TIME_FORMAT);
167         cxml.append("    <Timestamp>").append(date.format(d)).append("T").append(time.format(d)).append("+05:30").append("</Timestamp>\n");
168 
169         cxml.append("    <Authentication>\n");
170         cxml.append("      <Identity>").append(b2bPurchaseOrderIdentity).append("</Identity>\n");
171         cxml.append("      <SharedSecret>").append(password).append("</SharedSecret>\n");
172         cxml.append("    </Authentication>\n");
173         cxml.append("  </Header>\n");
174         cxml.append("  <PurchaseOrder>\n");
175         cxml.append("    <POHeader>\n");
176         cxml.append("      <PONumber>").append(purchaseOrder.getPurapDocumentIdentifier()).append("</PONumber>\n");
177         cxml.append("      <Requestor>\n");
178         cxml.append("        <UserProfile username=\"").append(requisitionInitiatorPrincipalId.toUpperCase()).append("\">\n");
179         cxml.append("        </UserProfile>\n");
180         cxml.append("      </Requestor>\n");
181         cxml.append("      <Priority>High</Priority>\n");
182         cxml.append("      <AccountingDate>").append(purchaseOrder.getPurchaseOrderCreateTimestamp()).append("</AccountingDate>\n");
183 
184         /** *** SUPPLIER SECTION **** */
185         cxml.append("      <Supplier id=\"").append(purchaseOrder.getExternalOrganizationB2bSupplierIdentifier()).append("\">\n");
186         cxml.append("        <DUNS>").append(vendorDuns).append("</DUNS>\n");
187         cxml.append("        <SupplierNumber>").append(purchaseOrder.getVendorNumber()).append("</SupplierNumber>\n");
188 
189         // Type attribute is required. Valid values: main and technical. Only main will be considered for POImport.
190         cxml.append("        <ContactInfo type=\"main\">\n");
191         // TelephoneNumber is required. With all fields, only numeric digits will be stored. Non-numeric characters are allowed, but
192         // will be stripped before storing.
193         cxml.append("          <Phone>\n");
194         cxml.append("            <TelephoneNumber>\n");
195         cxml.append("              <CountryCode>1</CountryCode>\n");
196         if (contractManager.getContractManagerPhoneNumber().length() > 4) {
197             cxml.append("              <AreaCode>").append(contractManager.getContractManagerPhoneNumber().substring(0, 3)).append("</AreaCode>\n");
198             cxml.append("              <Number>").append(contractManager.getContractManagerPhoneNumber().substring(3)).append("</Number>\n");
199         }
200         else {
201             LOG.error("getCxml() The phone number is invalid for this contract manager: " + contractManager.getContractManagerUserIdentifier() + " " + contractManager.getContractManagerName());
202             cxml.append("              <AreaCode>555</AreaCode>\n");
203             cxml.append("              <Number>").append(contractManager.getContractManagerPhoneNumber()).append("</Number>\n");
204         }
205         cxml.append("            </TelephoneNumber>\n");
206         cxml.append("          </Phone>\n");
207         cxml.append("        </ContactInfo>\n");
208         cxml.append("      </Supplier>\n");
209 
210         /** *** BILL TO SECTION **** */
211         cxml.append("      <BillTo>\n");
212         cxml.append("        <Address>\n");
213         cxml.append("          <TemplateName>Bill To</TemplateName>\n");
214         cxml.append("          <AddressCode>").append(purchaseOrder.getDeliveryCampusCode()).append("</AddressCode>\n");
215         // Contact - There can be 0-5 Contact elements. The label attribute is optional.
216         cxml.append("          <Contact label=\"FirstName\" linenumber=\"1\"><![CDATA[Accounts]]></Contact>\n");
217         cxml.append("          <Contact label=\"LastName\" linenumber=\"2\"><![CDATA[Payable]]></Contact>\n");
218         cxml.append("          <Contact label=\"Company\" linenumber=\"3\"><![CDATA[").append(purchaseOrder.getBillingName().trim()).append("]]></Contact>\n");
219         // since email address is not required, we need to check whether its empty; if yes, don't add it
220         if (!StringUtils.isEmpty(purchaseOrder.getBillingEmailAddress())) {
221             cxml.append("          <Contact label=\"Email\" linenumber=\"4\"><![CDATA[").append(purchaseOrder.getBillingEmailAddress().trim()).append("]]></Contact>\n");
222         }
223         // since phone number is not required, we need to check whether its empty; if yes, don't add it
224         if (!StringUtils.isEmpty(purchaseOrder.getBillingPhoneNumber())) {
225             cxml.append("          <Contact label=\"Phone\" linenumber=\"5\"><![CDATA[").append(purchaseOrder.getBillingPhoneNumber().trim()).append("]]></Contact>\n");
226         }
227         // There must be 1-5 AddressLine elements. The label attribute is optional.
228         cxml.append("          <AddressLine label=\"Street1\" linenumber=\"1\"><![CDATA[").append(purchaseOrder.getBillingLine1Address()).append("]]></AddressLine>\n");
229         cxml.append("          <AddressLine label=\"Street2\" linenumber=\"2\"><![CDATA[").append(purchaseOrder.getBillingLine2Address()).append("]]></AddressLine>\n");
230         cxml.append("          <City><![CDATA[").append(purchaseOrder.getBillingCityName()).append("]]></City>\n"); // Required.
231         cxml.append("          <State>").append(purchaseOrder.getBillingStateCode()).append("</State>\n");
232         cxml.append("          <PostalCode>").append(purchaseOrder.getBillingPostalCode()).append("</PostalCode>\n"); // Required.
233         cxml.append("          <Country isocountrycode=\"").append(purchaseOrder.getBillingCountryCode()).append("\">").append(purchaseOrder.getBillingCountryCode()).append("</Country>\n");
234         cxml.append("        </Address>\n");
235         cxml.append("      </BillTo>\n");
236 
237         /** *** SHIP TO SECTION **** */
238         cxml.append("      <ShipTo>\n");
239         cxml.append("        <Address>\n");
240         cxml.append("          <TemplateName>Ship To</TemplateName>\n");
241         // AddressCode. A code to identify the address, that is sent to the supplier.
242         cxml.append("          <AddressCode>").append(purchaseOrder.getDeliveryCampusCode()).append(purchaseOrder.getOrganizationCode()).append("</AddressCode>\n");
243         cxml.append("          <Contact label=\"Name\" linenumber=\"1\"><![CDATA[").append(purchaseOrder.getDeliveryToName().trim()).append("]]></Contact>\n");
244         cxml.append("          <Contact label=\"PurchasingEmail\" linenumber=\"2\"><![CDATA[").append(contractManagerEmail).append("]]></Contact>\n");
245         if (ObjectUtils.isNotNull(purchaseOrder.getInstitutionContactEmailAddress())) {
246             cxml.append("          <Contact label=\"ContactEmail\" linenumber=\"3\"><![CDATA[").append(purchaseOrder.getInstitutionContactEmailAddress()).append("]]></Contact>\n");
247         }
248         else {
249             cxml.append("          <Contact label=\"ContactEmail\" linenumber=\"3\"><![CDATA[").append(purchaseOrder.getRequestorPersonEmailAddress()).append("]]></Contact>\n");
250         }
251         if (ObjectUtils.isNotNull(purchaseOrder.getInstitutionContactPhoneNumber())) {
252             cxml.append("          <Contact label=\"Phone\" linenumber=\"4\"><![CDATA[").append(purchaseOrder.getInstitutionContactPhoneNumber().trim()).append("]]></Contact>\n");
253         }
254         else {
255             cxml.append("          <Contact label=\"Phone\" linenumber=\"4\"><![CDATA[").append(purchaseOrder.getRequestorPersonPhoneNumber()).append("]]></Contact>\n");
256         }
257 
258         //check indicator to decide if receiving or delivery address should be sent to the vendor
259         if (purchaseOrder.getAddressToVendorIndicator()) {  //use receiving address
260             cxml.append("          <AddressLine label=\"Street1\" linenumber=\"1\"><![CDATA[").append(purchaseOrder.getReceivingName().trim()).append("]]></AddressLine>\n");
261             cxml.append("          <AddressLine label=\"Street2\" linenumber=\"2\"><![CDATA[").append(purchaseOrder.getReceivingLine1Address().trim()).append("]]></AddressLine>\n");
262             if (ObjectUtils.isNull(purchaseOrder.getReceivingLine2Address())) {
263                 cxml.append("          <AddressLine label=\"Street3\" linenumber=\"3\"><![CDATA[").append(" ").append("]]></AddressLine>\n");
264             }
265             else {
266                 cxml.append("          <AddressLine label=\"Street3\" linenumber=\"3\"><![CDATA[").append(purchaseOrder.getReceivingLine2Address()).append("]]></AddressLine>\n");
267             }
268             cxml.append("          <City><![CDATA[").append(purchaseOrder.getReceivingCityName().trim()).append("]]></City>\n");
269             cxml.append("          <State>").append(purchaseOrder.getReceivingStateCode()).append("</State>\n");
270             cxml.append("          <PostalCode>").append(purchaseOrder.getReceivingPostalCode()).append("</PostalCode>\n");
271             cxml.append("          <Country isocountrycode=\"").append(purchaseOrder.getReceivingCountryCode()).append("\">").append(purchaseOrder.getReceivingCountryCode()).append("</Country>\n");
272         }
273         else { //use final delivery address
274             /* replaced this with getBuildingLine so institutions can customize what info they need on this line
275             if (StringUtils.isNotEmpty(purchaseOrder.getDeliveryBuildingName())) {
276                 cxml.append("          <Contact label=\"Building\" linenumber=\"5\"><![CDATA[").append(purchaseOrder.getDeliveryBuildingName()).append(" (").append(purchaseOrder.getDeliveryBuildingCode()).append(")]]></Contact>\n");
277             }
278             */
279             cxml.append(getBuildingLine(purchaseOrder));
280             cxml.append("          <AddressLine label=\"Street1\" linenumber=\"1\"><![CDATA[").append(purchaseOrder.getDeliveryBuildingLine1Address().trim()).append("]]></AddressLine>\n");
281             cxml.append("          <AddressLine label=\"Street2\" linenumber=\"2\"><![CDATA[Room #").append(purchaseOrder.getDeliveryBuildingRoomNumber().trim()).append("]]></AddressLine>\n");
282             cxml.append("          <AddressLine label=\"Company\" linenumber=\"4\"><![CDATA[").append(purchaseOrder.getBillingName().trim()).append("]]></AddressLine>\n");
283             if (ObjectUtils.isNull(purchaseOrder.getDeliveryBuildingLine2Address())) {
284                 cxml.append("          <AddressLine label=\"Street3\" linenumber=\"3\"><![CDATA[").append(" ").append("]]></AddressLine>\n");
285             }
286             else {
287                 cxml.append("          <AddressLine label=\"Street3\" linenumber=\"3\"><![CDATA[").append(purchaseOrder.getDeliveryBuildingLine2Address()).append("]]></AddressLine>\n");
288             }
289             cxml.append("          <City><![CDATA[").append(purchaseOrder.getDeliveryCityName().trim()).append("]]></City>\n");
290             cxml.append("          <State>").append(purchaseOrder.getDeliveryStateCode()).append("</State>\n");
291             cxml.append("          <PostalCode>").append(purchaseOrder.getDeliveryPostalCode()).append("</PostalCode>\n");
292             cxml.append("          <Country isocountrycode=\"").append(purchaseOrder.getDeliveryCountryCode()).append("\">").append(purchaseOrder.getDeliveryCountryCode()).append("</Country>\n");
293         }
294 
295         cxml.append("        </Address>\n");
296         cxml.append("      </ShipTo>\n");
297         cxml.append("    </POHeader>\n");
298 
299         /** *** Items Section **** */
300         List detailList = purchaseOrder.getItems();
301         for (Iterator iter = detailList.iterator(); iter.hasNext();) {
302             PurchaseOrderItem poi = (PurchaseOrderItem) iter.next();
303             if ((ObjectUtils.isNotNull(poi.getItemType())) && poi.getItemType().isLineItemIndicator()) {
304                 cxml.append("    <POLine linenumber=\"").append(poi.getItemLineNumber()).append("\">\n");
305                 cxml.append("      <Item>\n");
306                 // CatalogNumber - This is a string that the supplier uses to identify the item (i.e., SKU). Optional.
307                 cxml.append("        <CatalogNumber><![CDATA[").append(poi.getItemCatalogNumber()).append("]]></CatalogNumber>\n");
308                 if (ObjectUtils.isNotNull(poi.getItemAuxiliaryPartIdentifier())) {
309                     cxml.append("        <AuxiliaryCatalogNumber><![CDATA[").append(poi.getItemAuxiliaryPartIdentifier()).append("]]></AuxiliaryCatalogNumber>\n");
310                 }
311                 cxml.append("        <Description><![CDATA[").append(poi.getItemDescription()).append("]]></Description>\n"); // Required.
312                 cxml.append("        <ProductUnitOfMeasure type=\"supplier\"><Measurement><MeasurementValue><![CDATA[").append(poi.getItemUnitOfMeasureCode()).append("]]></MeasurementValue></Measurement></ProductUnitOfMeasure>\n");
313                 cxml.append("        <ProductUnitOfMeasure type=\"system\"><Measurement><MeasurementValue><![CDATA[").append(poi.getItemUnitOfMeasureCode()).append("]]></MeasurementValue></Measurement></ProductUnitOfMeasure>\n");
314                 // ProductReferenceNumber - Unique id for hosted products in SelectSite
315                 if (poi.getExternalOrganizationB2bProductTypeName().equals("Punchout")) {
316                     cxml.append("        <ProductReferenceNumber>null</ProductReferenceNumber>\n");
317                 }
318                 else {
319                     cxml.append("        <ProductReferenceNumber>").append(poi.getExternalOrganizationB2bProductReferenceNumber()).append("</ProductReferenceNumber>\n");
320                 }
321                 // ProductType - Describes the type of the product or service. Valid values: Catalog, Form, Punchout. Mandatory.
322                 cxml.append("        <ProductType>").append(poi.getExternalOrganizationB2bProductTypeName()).append("</ProductType>\n");
323                 cxml.append("      </Item>\n");
324                 cxml.append("      <Quantity>").append(poi.getItemQuantity()).append("</Quantity>\n");
325                 // LineCharges - All the monetary charges for this line, including the price, tax, shipping, and handling.
326                 // Required.
327                 cxml.append("      <LineCharges>\n");
328                 cxml.append("        <UnitPrice>\n");
329                 cxml.append("          <Money currency=\"USD\">").append(poi.getItemUnitPrice()).append("</Money>\n");
330                 cxml.append("        </UnitPrice>\n");
331                 cxml.append("      </LineCharges>\n");
332                 cxml.append("    </POLine>\n");
333             }
334         }
335 
336         cxml.append("  </PurchaseOrder>\n");
337         cxml.append("</PurchaseOrderMessage>");
338 
339         if (LOG.isDebugEnabled()) {
340             LOG.debug("getCxml(): cXML for po number " + purchaseOrder.getPurapDocumentIdentifier() + ":\n" + cxml.toString());
341         }
342 
343         return cxml.toString();
344     }
345 
346     /**
347      * @see org.kuali.kfs.module.purap.document.service.B2BPurchaseOrderService#verifyCxmlPOData(org.kuali.kfs.module.purap.document.PurchaseOrderDocument,
348      *      org.kuali.rice.kim.api.identity.Person, java.lang.String, org.kuali.kfs.vnd.businessobject.ContractManager,
349      *      java.lang.String, java.lang.String)
350      */
351     public String verifyCxmlPOData(PurchaseOrderDocument purchaseOrder, String requisitionInitiatorPrincipalName, String password, ContractManager contractManager, String contractManagerEmail, String vendorDuns) {
352         StringBuffer errors = new StringBuffer();
353 
354         if (ObjectUtils.isNull(purchaseOrder)) {
355             LOG.error("verifyCxmlPOData()  The Purchase Order is null.");
356             errors.append("Error occurred retrieving Purchase Order\n");
357             return errors.toString();
358         }
359         if (ObjectUtils.isNull(contractManager)) {
360             LOG.error("verifyCxmlPOData()  The contractManager is null.");
361             errors.append("Error occurred retrieving Contract Manager\n");
362             return errors.toString();
363         }
364         if (StringUtils.isEmpty(password)) {
365             LOG.error("verifyCxmlPOData()  The B2B PO password is required for the cXML PO but is missing.");
366             errors.append("Missing Data: B2B PO password\n");
367         }
368         if (ObjectUtils.isNull(purchaseOrder.getPurapDocumentIdentifier())) {
369             LOG.error("verifyCxmlPOData()  The purchase order Id is required for the cXML PO but is missing.");
370             errors.append("Missing Data: Purchase Order ID\n");
371         }
372         if (StringUtils.isEmpty(requisitionInitiatorPrincipalName)) {
373             LOG.error("verifyCxmlPOData()  The requisition initiator principal name is required for the cXML PO but is missing.");
374             errors.append("Missing Data: Requisition Initiator Principal Name\n");
375         }
376         if (ObjectUtils.isNull(purchaseOrder.getPurchaseOrderCreateTimestamp())) {
377             LOG.error("verifyCxmlPOData()  The PO create date is required for the cXML PO but is null.");
378             errors.append("Create Date\n");
379         }
380         if (StringUtils.isEmpty(contractManager.getContractManagerPhoneNumber())) {
381             LOG.error("verifyCxmlPOData()  The contract manager phone number is required for the cXML PO but is missing.");
382             errors.append("Missing Data: Contract Manager Phone Number\n");
383         }
384         if (StringUtils.isEmpty(contractManager.getContractManagerName())) {
385             LOG.error("verifyCxmlPOData()  The contract manager name is required for the cXML PO but is missing.");
386             errors.append("Missing Data: Contract Manager Name\n");
387         }
388         if (StringUtils.isEmpty(purchaseOrder.getDeliveryCampusCode())) {
389             LOG.error("verifyCxmlPOData()  The Delivery Campus Code is required for the cXML PO but is missing.");
390             errors.append("Missing Data: Delivery Campus Code\n");
391         }
392         if (StringUtils.isEmpty(purchaseOrder.getBillingName())) {
393             LOG.error("verifyCxmlPOData()  The Delivery Billing Name is required for the cXML PO but is missing.");
394             errors.append("Missing Data: Delivery Billing Name\n");
395         }
396         if (StringUtils.isEmpty(purchaseOrder.getBillingLine1Address())) {
397             LOG.error("verifyCxmlPOData()  The Billing Line 1 Address is required for the cXML PO but is missing.");
398             errors.append("Missing Data: Billing Line 1 Address\n");
399         }
400         if (StringUtils.isEmpty(purchaseOrder.getBillingLine2Address())) {
401             LOG.error("verifyCxmlPOData()  The Billing Line 2 Address is required for the cXML PO but is missing.");
402             errors.append("Missing Data: Billing Line 2 Address\n");
403         }
404         if (StringUtils.isEmpty(purchaseOrder.getBillingCityName())) {
405             LOG.error("verifyCxmlPOData()  The Billing Address City Name is required for the cXML PO but is missing.");
406             errors.append("Missing Data: Billing Address City Name\n");
407         }
408         if (StringUtils.isEmpty(purchaseOrder.getBillingStateCode())) {
409             LOG.error("verifyCxmlPOData()  The Billing Address State Code is required for the cXML PO but is missing.");
410             errors.append("Missing Data: Billing Address State Code\n");
411         }
412         if (StringUtils.isEmpty(purchaseOrder.getBillingPostalCode())) {
413             LOG.error("verifyCxmlPOData()  The Billing Address Postal Code is required for the cXML PO but is missing.");
414             errors.append("Missing Data: Billing Address Postal Code\n");
415         }
416         if (StringUtils.isEmpty(purchaseOrder.getDeliveryToName())) {
417             LOG.error("verifyCxmlPOData()  The Delivery To Name is required for the cXML PO but is missing.");
418             errors.append("Missing Data: Delivery To Name\n");
419         }
420         if (StringUtils.isEmpty(contractManagerEmail)) {
421             LOG.error("verifyCxmlPOData()  The Contract Manager Email is required for the cXML PO but is missing.");
422             errors.append("Missing Data: Contract Manager Email\n");
423         }
424         if (StringUtils.isEmpty(purchaseOrder.getRequestorPersonEmailAddress())) {
425             LOG.error("verifyCxmlPOData()  The Requesting Person Email Address is required for the cXML PO but is missing.");
426             errors.append("Missing Data: Requesting Person Email Address\n");
427         }
428         if (StringUtils.isEmpty(purchaseOrder.getRequestorPersonPhoneNumber())) {
429             LOG.error("verifyCxmlPOData()  The Requesting Person Phone Number is required for the cXML PO but is missing.");
430             errors.append("Missing Data: Requesting Person Phone Number\n");
431         }
432         if (StringUtils.isEmpty(purchaseOrder.getDeliveryBuildingLine1Address())) {
433             LOG.error("verifyCxmlPOData()  The Delivery Line 1 Address is required for the cXML PO but is missing.");
434             errors.append("Missing Data: Delivery Line 1 Address\n");
435         }
436         if (StringUtils.isEmpty(purchaseOrder.getDeliveryToName())) {
437             LOG.error("verifyCxmlPOData()  The Delivery To Name is required for the cXML PO but is missing.");
438             errors.append("Missing Data: Delivery To Name\n");
439         }
440         if (StringUtils.isEmpty(purchaseOrder.getDeliveryCityName())) {
441             LOG.error("verifyCxmlPOData()  The Delivery City Name is required for the cXML PO but is missing.");
442             errors.append("Missing Data: Delivery City Name\n");
443         }
444         if (StringUtils.isEmpty(purchaseOrder.getDeliveryStateCode())) {
445             LOG.error("verifyCxmlPOData()  The Delivery State is required for the cXML PO but is missing.");
446             errors.append("Missing Data: Delivery State\n");
447         }
448         if (StringUtils.isEmpty(purchaseOrder.getDeliveryPostalCode())) {
449             LOG.error("verifyCxmlPOData()  The Delivery Postal Code is required for the cXML PO but is missing.");
450             errors.append("Missing Data: Delivery Postal Code\n");
451         }
452 
453         // verify item data
454         List detailList = purchaseOrder.getItems();
455         for (Iterator iter = detailList.iterator(); iter.hasNext();) {
456             PurchaseOrderItem poi = (PurchaseOrderItem) iter.next();
457             if (ObjectUtils.isNotNull(poi.getItemType()) && poi.getItemType().isLineItemIndicator()) {
458                 if (ObjectUtils.isNull(poi.getItemLineNumber())) {
459                     LOG.error("verifyCxmlPOData()  The Item Line Number is required for the cXML PO but is missing.");
460                     errors.append("Missing Data: Item Line Number\n");
461                 }
462                 if (StringUtils.isEmpty(poi.getItemCatalogNumber())) {
463                     LOG.error("verifyCxmlPOData()  The Catalog Number for item number " + poi.getItemLineNumber() + " is required for the cXML PO but is missing.");
464                     errors.append("Missing Data: Item#" + poi.getItemLineNumber() + " - Catalog Number\n");
465                 }
466                 if (StringUtils.isEmpty(poi.getItemDescription())) {
467                     LOG.error("verifyCxmlPOData()  The Description for item number " + poi.getItemLineNumber() + " is required for the cXML PO but is missing.");
468                     errors.append("Missing Data: Item#" + poi.getItemLineNumber() + " - Description\n");
469                 }
470                 if (StringUtils.isEmpty(poi.getItemUnitOfMeasureCode())) {
471                     LOG.error("verifyCxmlPOData()  The Unit Of Measure Code for item number " + poi.getItemLineNumber() + " is required for the cXML PO but is missing.");
472                     errors.append("Missing Data: Item#" + poi.getItemLineNumber() + " - Unit Of Measure\n");
473                 }
474                 if (StringUtils.isEmpty(poi.getExternalOrganizationB2bProductTypeName())) {
475                     LOG.error("verifyCxmlPOData()  The External Org B2B Product Type Name for item number " + poi.getItemLineNumber() + " is required for the cXML PO but is missing.");
476                     errors.append("Missing Data: Item#" + poi.getItemLineNumber() + " - External Org B2B Product Type Name\n");
477                 }
478                 if (poi.getItemQuantity() == null) {
479                     LOG.error("verifyCxmlPOData()  The Order Quantity for item number " + poi.getItemLineNumber() + " is required for the cXML PO but is missing.");
480                     errors.append("Missing Data: Item#" + poi.getItemLineNumber() + " - Order Quantity\n");
481                 }
482                 if (poi.getItemUnitPrice() == null) {
483                     LOG.error("verifyCxmlPOData()  The Unit Price for item number " + poi.getItemLineNumber() + " is required for the cXML PO but is missing.");
484                     errors.append("Missing Data: Item#" + poi.getItemLineNumber() + " - Unit Price\n");
485                 }
486             }
487         } // end item looping
488 
489         return errors.toString();
490     }
491 
492     /**
493      * Retrieve the Contract Manager's email
494      */
495     protected String getContractManagerEmail(ContractManager cm) {
496 
497         Person contractManager = getPersonService().getPerson(cm.getContractManagerUserIdentifier());
498         if (ObjectUtils.isNotNull(contractManager)) {
499             return contractManager.getEmailAddressUnmasked();
500         }
501         return "";
502     }
503 
504     /**
505      * Retrieve the Requisition Initiator Principal Name
506      */
507     protected String getRequisitionInitiatorPrincipal(String requisitionInitiatorPrincipalId) {
508 
509         Principal requisitionInitiator = KimApiServiceLocator.getIdentityService().getPrincipal(requisitionInitiatorPrincipalId);
510         if (ObjectUtils.isNotNull(requisitionInitiator)) {
511             return requisitionInitiator.getPrincipalName();
512         }
513         return "";
514     }
515 
516     public void setRequisitionService(RequisitionService requisitionService) {
517         this.requisitionService = requisitionService;
518     }
519 
520     public void setParameterService(ParameterService parameterService) {
521         this.parameterService = parameterService;
522     }
523 
524     public void setB2bDao(B2BDao b2bDao) {
525         this.b2bDao = b2bDao;
526     }
527 
528     /**
529      * @return Returns the personService.
530      */
531     protected PersonService getPersonService() {
532         if(personService==null) {
533             personService = SpringContext.getBean(PersonService.class);
534         }
535         return personService;
536     }
537 
538     public void setB2bEnvironment(String environment) {
539         b2bEnvironment = environment;
540     }
541 
542     public void setB2bUserAgent(String userAgent) {
543         b2bUserAgent = userAgent;
544     }
545 
546     public void setB2bPurchaseOrderURL(String purchaseOrderURL) {
547         b2bPurchaseOrderURL = purchaseOrderURL;
548     }
549 
550     public void setB2bPurchaseOrderIdentity(String b2bPurchaseOrderIdentity) {
551         this.b2bPurchaseOrderIdentity = b2bPurchaseOrderIdentity;
552     }
553 
554     public void setB2bPurchaseOrderPassword(String purchaseOrderPassword) {
555         b2bPurchaseOrderPassword = purchaseOrderPassword;
556     }
557 
558     public String getB2bEnvironment() {
559         return b2bEnvironment;
560     }
561 
562     public String getB2bUserAgent() {
563         return b2bUserAgent;
564     }
565 
566     public String getB2bPurchaseOrderURL() {
567         return b2bPurchaseOrderURL;
568     }
569 
570     public String getB2bPurchaseOrderIdentity() {
571         return b2bPurchaseOrderIdentity;
572     }
573 
574     public String getB2bPurchaseOrderPassword() {
575         return b2bPurchaseOrderPassword;
576     }
577 
578     /* Returns the line for building. The default implementation includes both building name and building code in the line;
579      * institutions can override to customize according to what info they need on this line.
580      */
581     public String getBuildingLine(PurchaseOrderDocument purchaseOrder) {
582         StringBuffer line = new StringBuffer();
583         if (StringUtils.isNotEmpty(purchaseOrder.getDeliveryBuildingName())) {
584             line.append("          <Contact label=\"Building\" linenumber=\"5\"><![CDATA[").append(purchaseOrder.getDeliveryBuildingName()).append(" (").append(purchaseOrder.getDeliveryBuildingCode()).append(")]]></Contact>\n");
585         }
586         return line.toString();
587     }
588 }