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.KFSConstants;
40  import org.kuali.kfs.sys.context.SpringContext;
41  import org.kuali.kfs.vnd.businessobject.ContractManager;
42  import org.kuali.rice.core.api.config.property.ConfigurationService;
43  import org.kuali.rice.core.api.datetime.DateTimeService;
44  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
45  import org.kuali.rice.kew.api.WorkflowDocument;
46  import org.kuali.rice.kim.api.identity.Person;
47  import org.kuali.rice.kim.api.identity.PersonService;
48  import org.kuali.rice.kim.api.identity.principal.Principal;
49  import org.kuali.rice.kim.api.services.KimApiServiceLocator;
50  import org.kuali.rice.krad.util.ObjectUtils;
51  import org.springframework.transaction.annotation.Transactional;
52  
53  @Transactional
54  public class B2BPurchaseOrderServiceImpl implements B2BPurchaseOrderService {
55      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(B2BPurchaseOrderServiceImpl.class);
56  
57      private B2BDao b2bDao;
58      private RequisitionService requisitionService;
59      private ParameterService parameterService;
60      private PersonService personService;
61  
62      // injected values
63      private String b2bEnvironment;
64      private String b2bUserAgent;
65      private String b2bPurchaseOrderURL;
66      private String b2bPurchaseOrderIdentity;
67      private String b2bPurchaseOrderPassword;
68  
69      /**
70       * @see org.kuali.kfs.module.purap.document.service.B2BPurchaseOrderService#sendPurchaseOrder(org.kuali.kfs.module.purap.document.PurchaseOrderDocument)
71       */
72      public String sendPurchaseOrder(PurchaseOrderDocument purchaseOrder) {
73          /*
74           * IMPORTANT DESIGN NOTE: We need the contract manager's name, phone number, and e-mail address. B2B orders that don't
75           * 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
76           * always get the contract manager from the B2B contract associated with the order, and for B2B orders to ignore the
77           * contract manager field on the PO. We pull the name and phone number from the contract manager table and get the e-mail
78           * address from the user data.
79           */
80  
81          ContractManager contractManager = purchaseOrder.getVendorContract().getContractManager();
82          String contractManagerEmail = getContractManagerEmail(contractManager);
83  
84          String vendorDuns = purchaseOrder.getVendorDetail().getVendorDunsNumber();
85  
86          RequisitionDocument r = requisitionService.getRequisitionById(purchaseOrder.getRequisitionIdentifier());
87          WorkflowDocument reqWorkflowDoc = r.getDocumentHeader().getWorkflowDocument();
88          String requisitionInitiatorPrincipalId = getRequisitionInitiatorPrincipal(reqWorkflowDoc.getInitiatorPrincipalId());
89  
90          if (LOG.isDebugEnabled()) {
91              LOG.debug("sendPurchaseOrder(): b2bPurchaseOrderURL is " + b2bPurchaseOrderURL);
92          }
93  
94          String validateErrors = verifyCxmlPOData(purchaseOrder, requisitionInitiatorPrincipalId, b2bPurchaseOrderPassword, contractManager, contractManagerEmail, vendorDuns);
95          if (StringUtils.isEmpty(validateErrors)) {
96              return validateErrors;
97          }
98  
99          StringBuffer transmitErrors = new StringBuffer();
100 
101         try {
102             LOG.debug("sendPurchaseOrder() Generating cxml");
103             String cxml = getCxml(purchaseOrder, requisitionInitiatorPrincipalId, b2bPurchaseOrderPassword, contractManager, contractManagerEmail, vendorDuns);
104 
105             LOG.info("sendPurchaseOrder() Sending cxml\n" + cxml);
106             String responseCxml = b2bDao.sendPunchOutRequest(cxml, b2bPurchaseOrderURL);
107 
108             LOG.info("sendPurchaseOrder(): Response cXML for po #" + purchaseOrder.getPurapDocumentIdentifier() + ":\n" + responseCxml);
109 
110             PurchaseOrderResponse poResponse = B2BParserHelper.getInstance().parsePurchaseOrderResponse(responseCxml);
111             String statusText = poResponse.getStatusText();
112             if (LOG.isDebugEnabled()) {
113                 LOG.debug("sendPurchaseOrder(): statusText is " + statusText);
114             }
115             if ((ObjectUtils.isNull(statusText)) || (!"success".equalsIgnoreCase(statusText.trim()))) {
116                 LOG.error("sendPurchaseOrder(): PO cXML for po number " + purchaseOrder.getPurapDocumentIdentifier() + " failed sending to vendor: " + statusText);
117                 transmitErrors.append("Unable to send Purchase Order: " + statusText);
118 
119                 // find any additional error messages that might have been sent
120                 List errorMessages = poResponse.getPOResponseErrorMessages();
121                 if (ObjectUtils.isNotNull(errorMessages) && !errorMessages.isEmpty()) {
122                     for (Iterator iter = errorMessages.iterator(); iter.hasNext();) {
123                         String errorMessage = (String) iter.next();
124                         if (ObjectUtils.isNotNull(errorMessage)) {
125                             LOG.error("sendPurchaseOrder(): Error message for po number " + purchaseOrder.getPurapDocumentIdentifier() + ": " + errorMessage);
126                             transmitErrors.append("Error sending Purchase Order: " + errorMessage);
127                         }
128                     }
129                 }
130             }
131         }
132         catch (B2BConnectionException e) {
133             LOG.error("sendPurchaseOrder() Error connecting to b2b", e);
134             transmitErrors.append("Connection to vendor failed.");
135         }
136         catch (CxmlParseError e) {
137             LOG.error("sendPurchaseOrder() Error Parsing", e);
138             transmitErrors.append("Unable to read cxml returned from vendor.");
139         }
140         catch (Throwable e) {
141             LOG.error("sendPurchaseOrder() Unknown Error", e);
142             transmitErrors.append("Unexpected error occurred while attempting to transmit Purchase Order.");
143         }
144 
145         return transmitErrors.toString();
146     }
147 
148     /**
149      * @see org.kuali.kfs.module.purap.document.service.B2BPurchaseOrderService#getCxml(org.kuali.kfs.module.purap.document.PurchaseOrderDocument,
150      *      org.kuali.rice.kim.api.identity.Person, java.lang.String, org.kuali.kfs.vnd.businessobject.ContractManager,
151      *      java.lang.String, java.lang.String)
152      */
153     public String getCxml(PurchaseOrderDocument purchaseOrder, String requisitionInitiatorPrincipalId, String password, ContractManager contractManager, String contractManagerEmail, String vendorDuns) {
154 
155         StringBuffer cxml = new StringBuffer();
156         Date d = SpringContext.getBean(DateTimeService.class).getCurrentDate();
157         SimpleDateFormat date = PurApDateFormatUtils.getSimpleDateFormat(PurapConstants.NamedDateFormats.CXML_SIMPLE_DATE_FORMAT);
158         SimpleDateFormat time = PurApDateFormatUtils.getSimpleDateFormat(PurapConstants.NamedDateFormats.CXML_SIMPLE_TIME_FORMAT);
159 
160         cxml.append("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
161         cxml.append("<!DOCTYPE cXML SYSTEM \"http://xml.cXML.org/schemas/cXML/1.2.019/cXML.dtd\">\n");
162         // payloadID - can be whatever you would like it to be. Just make it unique.
163         cxml.append("<cXML payloadID=\"test@kuali.org\" timestamp=\"").append(date.format(d)).append("T").append(time.format(d)).append("+03:00").append("\" xml:lang=\"en-US\">\n");
164         cxml.append("  <Header>\n");
165         cxml.append("    <From>\n");
166         cxml.append("      <Credential domain=\"NetworkUserId\">\n");
167         cxml.append("        <Identity>").append(requisitionInitiatorPrincipalId.toUpperCase()).append("</Identity>\n");
168         cxml.append("      </Credential>\n");
169         cxml.append("    </From>\n");
170         cxml.append("    <To>\n");
171         cxml.append("      <Credential domain=\"DUNS\">\n");
172         cxml.append("        <Identity>").append(vendorDuns).append("</Identity>\n");
173         cxml.append("      </Credential>\n");
174         cxml.append("    </To>\n");
175         cxml.append("    <Sender>\n");
176         cxml.append("      <Credential domain=\"NetworkUserId\">\n");
177         cxml.append("        <Identity>").append(b2bPurchaseOrderIdentity).append("</Identity>\n");
178         cxml.append("        <SharedSecret>").append(password).append("</SharedSecret>\n");
179         cxml.append("      </Credential>\n");
180         cxml.append("      <UserAgent>Ariba.com Network V1.0</UserAgent>\n");
181         cxml.append("    </Sender>\n");
182         cxml.append("  </Header>\n");
183         // set deployment mode to test if not in production
184         if (isProduction()) {
185             cxml.append("  <Request>\n");
186         } else {
187             cxml.append("  <Request deploymentMode=\"test\">\n");
188         }
189         cxml.append("    <OrderRequest>\n");
190         cxml.append("      <OrderRequestHeader orderID=\"").append(purchaseOrder.getPurapDocumentIdentifier()).append("\" orderDate=\"").append(date.format(d)).append("\" type=\"new\">\n");
191         cxml.append("        <Total>\n");
192         cxml.append("          <Money currency=\"USD\">").append(purchaseOrder.getTotalDollarAmount()).append("</Money>\n");
193         cxml.append("        </Total>\n");
194 
195 
196         cxml.append("        <ShipTo>\n");
197         cxml.append("          <Address addressID=\"").append(purchaseOrder.getDeliveryCampusCode()).append(purchaseOrder.getOrganizationCode()).append("\">\n");
198         cxml.append("            <Name xml:lang=\"en\">Kuali</Name>\n");
199         cxml.append("            <PostalAddress name=\"defaul\">\n");
200         cxml.append("              <DeliverTo>").append(purchaseOrder.getDeliveryToName().trim()).append("</DeliverTo>\n");
201         if (StringUtils.isNotEmpty(purchaseOrder.getInstitutionContactEmailAddress())) {
202             cxml.append("              <DeliverTo><![CDATA[").append(purchaseOrder.getInstitutionContactEmailAddress()).append("]]></DeliverTo>\n");
203         }
204         else {
205             cxml.append("              <DeliverTo><![CDATA[").append(purchaseOrder.getRequestorPersonEmailAddress()).append("]]></DeliverTo>\n");
206         }
207         if (StringUtils.isNotEmpty(purchaseOrder.getInstitutionContactPhoneNumber())) {
208             cxml.append("              <DeliverTo><![CDATA[").append(purchaseOrder.getInstitutionContactPhoneNumber()).append("]]></DeliverTo>\n");
209         }
210         else {
211             cxml.append("              <DeliverTo><![CDATA[").append(purchaseOrder.getRequestorPersonPhoneNumber()).append("]]></DeliverTo>\n");
212         }
213 
214         //check indicator to decide if receiving or delivery address should be sent to the vendor
215         if (purchaseOrder.getAddressToVendorIndicator()) {  //use receiving address
216             if (StringUtils.isNotEmpty(purchaseOrder.getReceivingName())) {
217                 cxml.append("              <DeliverTo><![CDATA[").append(purchaseOrder.getReceivingName()).append("]]></DeliverTo>\n");
218             }
219             cxml.append("              <Street><![CDATA[").append(purchaseOrder.getReceivingLine1Address().trim()).append("]]></Street>\n");
220             if (StringUtils.isNotEmpty(purchaseOrder.getReceivingLine2Address())) {
221                 cxml.append("              <Street><![CDATA[").append(purchaseOrder.getReceivingLine2Address().trim()).append("]]></Street>\n");
222             }
223             cxml.append("              <City><![CDATA[").append(purchaseOrder.getReceivingCityName().trim()).append("]]></City>\n");
224             cxml.append("              <State>").append(purchaseOrder.getReceivingStateCode()).append("</State>\n");
225             cxml.append("              <PostalCode>").append(purchaseOrder.getReceivingPostalCode()).append("</PostalCode>\n");
226             cxml.append("              <Country isoCountryCode=\"").append(purchaseOrder.getReceivingCountryCode()).append("\">").append(purchaseOrder.getReceivingCountryCode()).append("</Country>\n");
227         }
228         else { //use final delivery address
229             if (StringUtils.isNotEmpty(purchaseOrder.getDeliveryBuildingName())) {
230                 cxml.append("              <DeliverTo><![CDATA[").append(purchaseOrder.getDeliveryBuildingName()).append(" (").append(purchaseOrder.getDeliveryBuildingCode()).append(")]]></DeliverTo>\n");
231             }
232             cxml.append("              <Street><![CDATA[").append(purchaseOrder.getDeliveryBuildingLine1Address().trim()).append("]]></Street>\n");
233             if (StringUtils.isNotEmpty(purchaseOrder.getDeliveryBuildingLine2Address())) {
234                 cxml.append("              <Street><![CDATA[").append(purchaseOrder.getDeliveryBuildingLine2Address().trim()).append("]]></Street>\n");
235             }
236             if (StringUtils.isNotEmpty(purchaseOrder.getDeliveryBuildingRoomNumber())) {
237                 cxml.append("              <Street><![CDATA[").append(purchaseOrder.getDeliveryBuildingRoomNumber().trim()).append("]]></Street>\n");
238             }
239             cxml.append("              <City><![CDATA[").append(purchaseOrder.getDeliveryCityName().trim()).append("]]></City>\n");
240             cxml.append("              <State>").append(purchaseOrder.getDeliveryStateCode()).append("</State>\n");
241             cxml.append("              <PostalCode>").append(purchaseOrder.getDeliveryPostalCode()).append("</PostalCode>\n");
242             cxml.append("              <Country isoCountryCode=\"").append(purchaseOrder.getDeliveryCountryCode()).append("\">").append(purchaseOrder.getDeliveryCountryName()).append("</Country>\n");
243         }
244         cxml.append("            </PostalAddress>\n");
245         cxml.append("          </Address>\n");
246         cxml.append("        </ShipTo>\n");
247 
248 
249         cxml.append("        <BillTo>\n");
250         cxml.append("          <Address addressID=\"").append(purchaseOrder.getDeliveryCampusCode()).append("\">\n");
251         cxml.append("            <Name xml:lang=\"en\"><![CDATA[").append(purchaseOrder.getBillingName().trim()).append("]]></Name>\n");
252         cxml.append("            <PostalAddress name=\"defaul\">\n");
253         cxml.append("              <Street><![CDATA[").append(purchaseOrder.getBillingLine1Address().trim()).append("]]></Street>\n");
254         if (StringUtils.isNotEmpty(purchaseOrder.getBillingLine2Address())) {
255             cxml.append("              <Street><![CDATA[").append(purchaseOrder.getBillingLine2Address().trim()).append("]]></Street>\n");
256         }
257         cxml.append("              <City><![CDATA[").append(purchaseOrder.getBillingCityName().trim()).append("]]></City>\n");
258         cxml.append("              <State>").append(purchaseOrder.getBillingStateCode()).append("</State>\n");
259         cxml.append("              <PostalCode>").append(purchaseOrder.getBillingPostalCode()).append("</PostalCode>\n");
260         cxml.append("              <Country isoCountryCode=\"").append(purchaseOrder.getBillingCountryCode()).append("\">").append(purchaseOrder.getBillingCountryName()).append("</Country>\n");
261         cxml.append("            </PostalAddress>\n");
262         cxml.append("          </Address>\n");
263         cxml.append("        </BillTo>\n");
264         cxml.append("        <Tax>\n");
265         cxml.append("          <Money currency=\"USD\">").append(purchaseOrder.getTotalTaxAmount()).append("</Money>\n");
266         cxml.append("          <Description xml:lang=\"en\">").append("tax description").append("</Description>\n");
267         cxml.append("        </Tax>\n");
268         cxml.append("        <Extrinsic name=\"username\">").append(requisitionInitiatorPrincipalId.toUpperCase()).append("</Extrinsic>\n");
269         cxml.append("        <Extrinsic name=\"BuyerPhone\">").append(contractManager.getContractManagerPhoneNumber()).append("</Extrinsic>\n");
270         cxml.append("        <Extrinsic name=\"SupplierNumber\">").append(purchaseOrder.getVendorNumber()).append("</Extrinsic>\n");
271         cxml.append("      </OrderRequestHeader>\n");
272 
273         for (Object tmpPoi : purchaseOrder.getItems()) {
274             PurchaseOrderItem poi = (PurchaseOrderItem) tmpPoi;
275             cxml.append("      <ItemOut quantity=\"").append(poi.getItemQuantity()).append("\" lineNumber=\"").append(poi.getItemLineNumber()).append("\">\n");
276             cxml.append("        <ItemID>\n");
277             cxml.append("          <SupplierPartID><![CDATA[").append(poi.getItemCatalogNumber()).append("]]></SupplierPartID>\n");
278             if (ObjectUtils.isNotNull(poi.getItemAuxiliaryPartIdentifier())) {
279                 cxml.append("          <SupplierPartAuxiliaryID><![CDATA[").append(poi.getItemAuxiliaryPartIdentifier()).append("]]></SupplierPartAuxiliaryID>\n");
280             }
281             cxml.append("        </ItemID>\n");
282             cxml.append("        <ItemDetail>\n");
283             cxml.append("          <UnitPrice>\n");
284             cxml.append("            <Money currency=\"USD\">").append(poi.getItemUnitPrice()).append("</Money>\n");
285             cxml.append("          </UnitPrice>\n");
286             cxml.append("          <Description xml:lang=\"en\"><![CDATA[").append(poi.getItemDescription()).append("]]></Description>\n"); // Required.
287             cxml.append("          <UnitOfMeasure><![CDATA[").append(poi.getItemUnitOfMeasureCode()).append("]]></UnitOfMeasure>\n");
288             cxml.append("          <Classification domain=\"UNSPSC\"></Classification>\n");
289             if (poi.getExternalOrganizationB2bProductTypeName().equals("Punchout")) {
290                 cxml.append("          <ManufacturerPartID></ManufacturerPartID>\n");
291             }
292             else {
293                 cxml.append("          <ManufacturerPartID>").append(poi.getExternalOrganizationB2bProductReferenceNumber()).append("</ManufacturerPartID>\n");
294             }
295             cxml.append("          <ManufacturerName>").append(poi.getExternalOrganizationB2bProductTypeName()).append("</ManufacturerName>\n");
296             cxml.append("        </ItemDetail>\n");
297             cxml.append("      </ItemOut>\n");
298         }
299 
300         cxml.append("    </OrderRequest>\n");
301         cxml.append("  </Request>\n");
302         cxml.append("</cXML>");
303 
304         if (LOG.isDebugEnabled()) {
305             LOG.debug("getCxml(): cXML for po number " + purchaseOrder.getPurapDocumentIdentifier() + ":\n" + cxml.toString());
306         }
307 
308         return cxml.toString();
309     }
310 
311     /**
312      * @see org.kuali.kfs.module.purap.document.service.B2BPurchaseOrderService#verifyCxmlPOData(org.kuali.kfs.module.purap.document.PurchaseOrderDocument,
313      *      org.kuali.rice.kim.api.identity.Person, java.lang.String, org.kuali.kfs.vnd.businessobject.ContractManager,
314      *      java.lang.String, java.lang.String)
315      */
316     public String verifyCxmlPOData(PurchaseOrderDocument purchaseOrder, String requisitionInitiatorPrincipalId, String password, ContractManager contractManager, String contractManagerEmail, String vendorDuns) {
317         StringBuffer errors = new StringBuffer();
318 
319         if (ObjectUtils.isNull(purchaseOrder)) {
320             LOG.error("verifyCxmlPOData()  The Purchase Order is null.");
321             errors.append("Error occurred retrieving Purchase Order\n");
322             return errors.toString();
323         }
324         if (ObjectUtils.isNull(contractManager)) {
325             LOG.error("verifyCxmlPOData()  The contractManager is null.");
326             errors.append("Error occurred retrieving Contract Manager\n");
327             return errors.toString();
328         }
329         if (StringUtils.isEmpty(password)) {
330             LOG.error("verifyCxmlPOData()  The B2B PO password is required for the cXML PO but is missing.");
331             errors.append("Missing Data: B2B PO password\n");
332         }
333         if (ObjectUtils.isNull(purchaseOrder.getPurapDocumentIdentifier())) {
334             LOG.error("verifyCxmlPOData()  The purchase order Id is required for the cXML PO but is missing.");
335             errors.append("Missing Data: Purchase Order ID\n");
336         }
337         if (StringUtils.isEmpty(requisitionInitiatorPrincipalId)) {
338             LOG.error("verifyCxmlPOData()  The requisition initiator principal name is required for the cXML PO but is missing.");
339             errors.append("Missing Data: Requisition Initiator Principal Name\n");
340         }
341         if (ObjectUtils.isNull(purchaseOrder.getPurchaseOrderCreateTimestamp())) {
342             LOG.error("verifyCxmlPOData()  The PO create date is required for the cXML PO but is null.");
343             errors.append("Create Date\n");
344         }
345         if (StringUtils.isEmpty(contractManager.getContractManagerPhoneNumber())) {
346             LOG.error("verifyCxmlPOData()  The contract manager phone number is required for the cXML PO but is missing.");
347             errors.append("Missing Data: Contract Manager Phone Number\n");
348         }
349         if (StringUtils.isEmpty(contractManager.getContractManagerName())) {
350             LOG.error("verifyCxmlPOData()  The contract manager name is required for the cXML PO but is missing.");
351             errors.append("Missing Data: Contract Manager Name\n");
352         }
353         if (StringUtils.isEmpty(purchaseOrder.getDeliveryCampusCode())) {
354             LOG.error("verifyCxmlPOData()  The Delivery Campus Code is required for the cXML PO but is missing.");
355             errors.append("Missing Data: Delivery Campus Code\n");
356         }
357         if (StringUtils.isEmpty(purchaseOrder.getBillingName())) {
358             LOG.error("verifyCxmlPOData()  The Delivery Billing Name is required for the cXML PO but is missing.");
359             errors.append("Missing Data: Delivery Billing Name\n");
360         }
361         if (StringUtils.isEmpty(purchaseOrder.getBillingLine1Address())) {
362             LOG.error("verifyCxmlPOData()  The Billing Line 1 Address is required for the cXML PO but is missing.");
363             errors.append("Missing Data: Billing Line 1 Address\n");
364         }
365         if (StringUtils.isEmpty(purchaseOrder.getBillingLine2Address())) {
366             LOG.error("verifyCxmlPOData()  The Billing Line 2 Address is required for the cXML PO but is missing.");
367             errors.append("Missing Data: Billing Line 2 Address\n");
368         }
369         if (StringUtils.isEmpty(purchaseOrder.getBillingCityName())) {
370             LOG.error("verifyCxmlPOData()  The Billing Address City Name is required for the cXML PO but is missing.");
371             errors.append("Missing Data: Billing Address City Name\n");
372         }
373         if (StringUtils.isEmpty(purchaseOrder.getBillingStateCode())) {
374             LOG.error("verifyCxmlPOData()  The Billing Address State Code is required for the cXML PO but is missing.");
375             errors.append("Missing Data: Billing Address State Code\n");
376         }
377         if (StringUtils.isEmpty(purchaseOrder.getBillingPostalCode())) {
378             LOG.error("verifyCxmlPOData()  The Billing Address Postal Code is required for the cXML PO but is missing.");
379             errors.append("Missing Data: Billing Address Postal Code\n");
380         }
381         if (StringUtils.isEmpty(purchaseOrder.getDeliveryToName())) {
382             LOG.error("verifyCxmlPOData()  The Delivery To Name is required for the cXML PO but is missing.");
383             errors.append("Missing Data: Delivery To Name\n");
384         }
385         if (StringUtils.isEmpty(contractManagerEmail)) {
386             LOG.error("verifyCxmlPOData()  The Contract Manager Email is required for the cXML PO but is missing.");
387             errors.append("Missing Data: Contract Manager Email\n");
388         }
389         if (StringUtils.isEmpty(purchaseOrder.getDeliveryToEmailAddress())) {
390             LOG.error("verifyCxmlPOData()  The Requesting Person Email Address is required for the cXML PO but is missing.");
391             errors.append("Missing Data: Requesting Person Email Address\n");
392         }
393         if (StringUtils.isEmpty(purchaseOrder.getDeliveryToPhoneNumber())) {
394             LOG.error("verifyCxmlPOData()  The Requesting Person Phone Number is required for the cXML PO but is missing.");
395             errors.append("Missing Data: Requesting Person Phone Number\n");
396         }
397         if (StringUtils.isEmpty(purchaseOrder.getDeliveryBuildingLine1Address())) {
398             LOG.error("verifyCxmlPOData()  The Delivery Line 1 Address is required for the cXML PO but is missing.");
399             errors.append("Missing Data: Delivery Line 1 Address\n");
400         }
401         if (StringUtils.isEmpty(purchaseOrder.getDeliveryToName())) {
402             LOG.error("verifyCxmlPOData()  The Delivery To Name is required for the cXML PO but is missing.");
403             errors.append("Missing Data: Delivery To Name\n");
404         }
405         if (StringUtils.isEmpty(purchaseOrder.getDeliveryCityName())) {
406             LOG.error("verifyCxmlPOData()  The Delivery City Name is required for the cXML PO but is missing.");
407             errors.append("Missing Data: Delivery City Name\n");
408         }
409         if (StringUtils.isEmpty(purchaseOrder.getDeliveryStateCode())) {
410             LOG.error("verifyCxmlPOData()  The Delivery State is required for the cXML PO but is missing.");
411             errors.append("Missing Data: Delivery State\n");
412         }
413         if (StringUtils.isEmpty(purchaseOrder.getDeliveryPostalCode())) {
414             LOG.error("verifyCxmlPOData()  The Delivery Postal Code is required for the cXML PO but is missing.");
415             errors.append("Missing Data: Delivery Postal Code\n");
416         }
417 
418         // verify item data
419         List detailList = purchaseOrder.getItems();
420         for (Iterator iter = detailList.iterator(); iter.hasNext();) {
421             PurchaseOrderItem poi = (PurchaseOrderItem) iter.next();
422             if (ObjectUtils.isNotNull(poi.getItemType()) && poi.getItemType().isLineItemIndicator()) {
423                 if (ObjectUtils.isNull(poi.getItemLineNumber())) {
424                     LOG.error("verifyCxmlPOData()  The Item Line Number is required for the cXML PO but is missing.");
425                     errors.append("Missing Data: Item Line Number\n");
426                 }
427                 if (StringUtils.isEmpty(poi.getItemCatalogNumber())) {
428                     LOG.error("verifyCxmlPOData()  The Catalog Number for item number " + poi.getItemLineNumber() + " is required for the cXML PO but is missing.");
429                     errors.append("Missing Data: Item#" + poi.getItemLineNumber() + " - Catalog Number\n");
430                 }
431                 if (StringUtils.isEmpty(poi.getItemDescription())) {
432                     LOG.error("verifyCxmlPOData()  The Description for item number " + poi.getItemLineNumber() + " is required for the cXML PO but is missing.");
433                     errors.append("Missing Data: Item#" + poi.getItemLineNumber() + " - Description\n");
434                 }
435                 if (StringUtils.isEmpty(poi.getItemUnitOfMeasureCode())) {
436                     LOG.error("verifyCxmlPOData()  The Unit Of Measure Code for item number " + poi.getItemLineNumber() + " is required for the cXML PO but is missing.");
437                     errors.append("Missing Data: Item#" + poi.getItemLineNumber() + " - Unit Of Measure\n");
438                 }
439                 if (StringUtils.isEmpty(poi.getExternalOrganizationB2bProductTypeName())) {
440                     LOG.error("verifyCxmlPOData()  The External Org B2B Product Type Name for item number " + poi.getItemLineNumber() + " is required for the cXML PO but is missing.");
441                     errors.append("Missing Data: Item#" + poi.getItemLineNumber() + " - External Org B2B Product Type Name\n");
442                 }
443                 if (poi.getItemQuantity() == null) {
444                     LOG.error("verifyCxmlPOData()  The Order Quantity for item number " + poi.getItemLineNumber() + " is required for the cXML PO but is missing.");
445                     errors.append("Missing Data: Item#" + poi.getItemLineNumber() + " - Order Quantity\n");
446                 }
447                 if (poi.getItemUnitPrice() == null) {
448                     LOG.error("verifyCxmlPOData()  The Unit Price for item number " + poi.getItemLineNumber() + " is required for the cXML PO but is missing.");
449                     errors.append("Missing Data: Item#" + poi.getItemLineNumber() + " - Unit Price\n");
450                 }
451             }
452         } // end item looping
453 
454         return errors.toString();
455     }
456 
457     /**
458      * Retrieve the Contract Manager's email
459      */
460     protected String getContractManagerEmail(ContractManager cm) {
461         Person contractManager = getPersonService().getPerson(cm.getContractManagerUserIdentifier());
462         if (ObjectUtils.isNotNull(contractManager)) {
463             return contractManager.getEmailAddressUnmasked();
464         }
465         return "";
466     }
467 
468     /**
469      * Retrieve the Requisition Initiator Principal Name
470      */
471     protected String getRequisitionInitiatorPrincipal(String requisitionInitiatorPrincipalId) {
472         Principal requisitionInitiator = KimApiServiceLocator.getIdentityService().getPrincipal(requisitionInitiatorPrincipalId);
473         if (ObjectUtils.isNotNull(requisitionInitiator)) {
474             return requisitionInitiator.getPrincipalName();
475         }
476         return "";
477     }
478 
479     /**
480      * @return Returns the personService.
481      */
482     protected PersonService getPersonService() {
483         if(personService==null) {
484             personService = SpringContext.getBean(PersonService.class);
485         }
486         return personService;
487     }
488 
489     public void setRequisitionService(RequisitionService requisitionService) {
490         this.requisitionService = requisitionService;
491     }
492 
493     public void setParameterService(ParameterService parameterService) {
494         this.parameterService = parameterService;
495     }
496 
497     public void setB2bDao(B2BDao b2bDao) {
498         this.b2bDao = b2bDao;
499     }
500 
501     /**
502      * Throws an exception if running on production
503      */
504     protected boolean isProduction() {
505         ConfigurationService configService = SpringContext.getBean(ConfigurationService.class);
506         return StringUtils.equals(configService.getPropertyValueAsString(KFSConstants.PROD_ENVIRONMENT_CODE_KEY), b2bEnvironment);
507     }
508 
509     public void setB2bEnvironment(String environment) {
510         b2bEnvironment = environment;
511     }
512 
513     public void setB2bUserAgent(String userAgent) {
514         b2bUserAgent = userAgent;
515     }
516 
517     public void setB2bPurchaseOrderURL(String purchaseOrderURL) {
518         b2bPurchaseOrderURL = purchaseOrderURL;
519     }
520 
521     public void setB2bPurchaseOrderIdentity(String b2bPurchaseOrderIdentity) {
522         this.b2bPurchaseOrderIdentity = b2bPurchaseOrderIdentity;
523     }
524 
525     public void setB2bPurchaseOrderPassword(String purchaseOrderPassword) {
526         b2bPurchaseOrderPassword = purchaseOrderPassword;
527     }
528 
529 }
530