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