001    /*
002     * Copyright 2011 The Kuali Foundation.
003     * 
004     * Licensed under the Educational Community License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     * 
008     * http://www.opensource.org/licenses/ecl2.php
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    package org.kuali.kfs.module.purap.document.service.impl;
017    
018    import java.text.SimpleDateFormat;
019    import java.util.Date;
020    import java.util.Iterator;
021    import java.util.List;
022    
023    import org.apache.commons.lang.StringUtils;
024    import org.kuali.kfs.module.purap.PurapConstants;
025    import org.kuali.kfs.module.purap.businessobject.PurchaseOrderItem;
026    import org.kuali.kfs.module.purap.dataaccess.B2BDao;
027    import org.kuali.kfs.module.purap.document.PurchaseOrderDocument;
028    import org.kuali.kfs.module.purap.document.RequisitionDocument;
029    import org.kuali.kfs.module.purap.document.service.B2BPurchaseOrderService;
030    import org.kuali.kfs.module.purap.document.service.RequisitionService;
031    import org.kuali.kfs.module.purap.exception.B2BConnectionException;
032    import org.kuali.kfs.module.purap.exception.CxmlParseError;
033    import org.kuali.kfs.module.purap.util.PurApDateFormatUtils;
034    import org.kuali.kfs.module.purap.util.cxml.B2BParserHelper;
035    import org.kuali.kfs.module.purap.util.cxml.PurchaseOrderResponse;
036    import org.kuali.kfs.sys.context.SpringContext;
037    import org.kuali.kfs.vnd.businessobject.ContractManager;
038    import org.kuali.rice.kim.bo.Person;
039    import org.kuali.rice.kim.service.PersonService;
040    import org.kuali.rice.kns.service.DateTimeService;
041    import org.kuali.rice.kns.service.ParameterService;
042    import org.kuali.rice.kns.util.ObjectUtils;
043    import org.kuali.rice.kns.workflow.service.KualiWorkflowDocument;
044    import org.springframework.transaction.annotation.Transactional;
045    
046    @Transactional
047    public class B2BPurchaseOrderSciquestServiceImpl implements B2BPurchaseOrderService {
048        private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(B2BPurchaseOrderSciquestServiceImpl.class);
049    
050        private B2BDao b2bDao;
051        private RequisitionService requisitionService;
052        private ParameterService parameterService;
053        private PersonService<Person> personService;
054    
055        // injected values
056        private String b2bEnvironment;
057        private String b2bUserAgent;
058        private String b2bPurchaseOrderURL;
059        private String b2bPurchaseOrderIdentity;
060        private String b2bPurchaseOrderPassword;
061    
062        /**
063         * @see org.kuali.kfs.module.purap.document.service.B2BPurchaseOrderService#sendPurchaseOrder(org.kuali.kfs.module.purap.document.PurchaseOrderDocument)
064         */
065        public String sendPurchaseOrder(PurchaseOrderDocument purchaseOrder) {
066            /*
067             * IMPORTANT DESIGN NOTE: We need the contract manager's name, phone number, and e-mail address. B2B orders that don't
068             * 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
069             * always get the contract manager from the B2B contract associated with the order, and for B2B orders to ignore the
070             * contract manager field on the PO. We pull the name and phone number from the contract manager table and get the e-mail
071             * address from the user data.
072             */
073    
074            ContractManager contractManager = purchaseOrder.getVendorContract().getContractManager();
075            String contractManagerEmail = getContractManagerEmail(contractManager);
076    
077            String vendorDuns = purchaseOrder.getVendorDetail().getVendorDunsNumber();
078    
079            RequisitionDocument r = requisitionService.getRequisitionById(purchaseOrder.getRequisitionIdentifier());
080            KualiWorkflowDocument reqWorkflowDoc = r.getDocumentHeader().getWorkflowDocument();
081    
082            LOG.debug("sendPurchaseOrder(): b2bPurchaseOrderURL is " + b2bPurchaseOrderURL);
083    
084            String validateErrors = verifyCxmlPOData(purchaseOrder, reqWorkflowDoc.getInitiatorNetworkId(), b2bPurchaseOrderPassword, contractManager, contractManagerEmail, vendorDuns);
085            if (!StringUtils.isEmpty(validateErrors)) {
086                return validateErrors;
087            }
088    
089            StringBuffer transmitErrors = new StringBuffer();
090    
091            try {
092                LOG.debug("sendPurchaseOrder() Generating cxml");
093                String cxml = getCxml(purchaseOrder, reqWorkflowDoc.getInitiatorNetworkId(), b2bPurchaseOrderPassword, contractManager, contractManagerEmail, vendorDuns);
094    
095                LOG.info("sendPurchaseOrder() Sending cxml\n" + cxml);
096                String responseCxml = b2bDao.sendPunchOutRequest(cxml, b2bPurchaseOrderURL);
097    
098                LOG.info("sendPurchaseOrder(): Response cXML for po #" + purchaseOrder.getPurapDocumentIdentifier() + ":\n" + responseCxml);
099    
100                PurchaseOrderResponse poResponse = B2BParserHelper.getInstance().parsePurchaseOrderResponse(responseCxml);
101                String statusText = poResponse.getStatusText();
102                LOG.debug("sendPurchaseOrder(): statusText is " + statusText);
103                if (ObjectUtils.isNull(statusText) || (!"success".equalsIgnoreCase(statusText.trim()))) {
104                    LOG.error("sendPurchaseOrder(): PO cXML for po number " + purchaseOrder.getPurapDocumentIdentifier() + " failed sending to SciQuest:\n" + statusText);
105                    transmitErrors.append("Unable to send Purchase Order: " + statusText);
106    
107                    // find any additional error messages that might have been sent
108                    List errorMessages = poResponse.getPOResponseErrorMessages();
109                    if (ObjectUtils.isNotNull(errorMessages) && !errorMessages.isEmpty()) {
110                        for (Iterator iter = errorMessages.iterator(); iter.hasNext();) {
111                            String errorMessage = (String) iter.next();
112                            if (ObjectUtils.isNotNull(errorMessage)) {
113                                LOG.error("sendPurchaseOrder(): SciQuest error message for po number " + purchaseOrder.getPurapDocumentIdentifier() + ": " + errorMessage);
114                                transmitErrors.append("Error sending Purchase Order: " + errorMessage);
115                            }
116                        }
117                    }
118                }
119            }
120            catch (B2BConnectionException e) {
121                LOG.error("sendPurchaseOrder() Error connecting to b2b", e);
122                transmitErrors.append("Connection to Sciquest failed.");
123            }
124            catch (CxmlParseError e) {
125                LOG.error("sendPurchaseOrder() Error Parsing", e);
126                transmitErrors.append("Unable to read cxml returned from Sciquest.");
127            }
128            catch (Throwable e) {
129                LOG.error("sendPurchaseOrder() Unknown Error", e);
130                transmitErrors.append("Unexpected error occurred while attempting to transmit Purchase Order.");
131            }
132    
133            return transmitErrors.toString();
134        }
135    
136        /**
137         * @see org.kuali.kfs.module.purap.document.service.B2BPurchaseOrderService#getCxml(org.kuali.kfs.module.purap.document.PurchaseOrderDocument,
138         *      org.kuali.rice.kim.bo.Person, java.lang.String, org.kuali.kfs.vnd.businessobject.ContractManager,
139         *      java.lang.String, java.lang.String)
140         */
141        public String getCxml(PurchaseOrderDocument purchaseOrder, String requisitionInitiatorId, String password, ContractManager contractManager, String contractManagerEmail, String vendorDuns) {
142    
143            StringBuffer cxml = new StringBuffer();
144    
145            cxml.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
146            cxml.append("<!DOCTYPE PurchaseOrderMessage SYSTEM \"PO.dtd\">\n");
147            cxml.append("<PurchaseOrderMessage version=\"2.0\">\n");
148            cxml.append("  <Header>\n");
149    
150            // MessageId - can be whatever you would like it to be. Just make it unique.
151            cxml.append("    <MessageId>KFS_cXML_PO</MessageId>\n");
152    
153            // Timestamp - it doesn't matter what's in the timezone, just that it's there (need "T" space between date/time)
154            Date d = SpringContext.getBean(DateTimeService.class).getCurrentDate();
155            SimpleDateFormat date = PurApDateFormatUtils.getSimpleDateFormat(PurapConstants.NamedDateFormats.CXML_SIMPLE_DATE_FORMAT);
156            SimpleDateFormat time = PurApDateFormatUtils.getSimpleDateFormat(PurapConstants.NamedDateFormats.CXML_SIMPLE_TIME_FORMAT);
157            cxml.append("    <Timestamp>").append(date.format(d)).append("T").append(time.format(d)).append("+05:30").append("</Timestamp>\n");
158    
159            cxml.append("    <Authentication>\n");
160            cxml.append("      <Identity>").append(b2bPurchaseOrderIdentity).append("</Identity>\n");
161            cxml.append("      <SharedSecret>").append(password).append("</SharedSecret>\n");
162            cxml.append("    </Authentication>\n");
163            cxml.append("  </Header>\n");
164            cxml.append("  <PurchaseOrder>\n");
165            cxml.append("    <POHeader>\n");
166            cxml.append("      <PONumber>").append(purchaseOrder.getPurapDocumentIdentifier()).append("</PONumber>\n");
167            cxml.append("      <Requestor>\n");
168            cxml.append("        <UserProfile username=\"").append(requisitionInitiatorId.toUpperCase()).append("\">\n");
169            cxml.append("        </UserProfile>\n");
170            cxml.append("      </Requestor>\n");
171            cxml.append("      <Priority>High</Priority>\n");
172            cxml.append("      <AccountingDate>").append(purchaseOrder.getPurchaseOrderCreateTimestamp()).append("</AccountingDate>\n");
173    
174            /** *** SUPPLIER SECTION **** */
175            cxml.append("      <Supplier id=\"").append(purchaseOrder.getExternalOrganizationB2bSupplierIdentifier()).append("\">\n");
176            cxml.append("        <DUNS>").append(vendorDuns).append("</DUNS>\n");
177            cxml.append("        <SupplierNumber>").append(purchaseOrder.getVendorNumber()).append("</SupplierNumber>\n");
178    
179            // Type attribute is required. Valid values: main and technical. Only main will be considered for POImport.
180            cxml.append("        <ContactInfo type=\"main\">\n");
181            // TelephoneNumber is required. With all fields, only numeric digits will be stored. Non-numeric characters are allowed, but
182            // will be stripped before storing.
183            cxml.append("          <Phone>\n");
184            cxml.append("            <TelephoneNumber>\n");
185            cxml.append("              <CountryCode>1</CountryCode>\n");
186            if (contractManager.getContractManagerPhoneNumber().length() > 4) {
187                cxml.append("              <AreaCode>").append(contractManager.getContractManagerPhoneNumber().substring(0, 3)).append("</AreaCode>\n");
188                cxml.append("              <Number>").append(contractManager.getContractManagerPhoneNumber().substring(3)).append("</Number>\n");
189            }
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            cxml.append("          <Contact label=\"Phone\" linenumber=\"4\"><![CDATA[").append(purchaseOrder.getBillingPhoneNumber().trim()).append("]]></Contact>\n");
210            // There must be 1-5 AddressLine elements. The label attribute is optional.
211            cxml.append("          <AddressLine label=\"Street1\" linenumber=\"1\"><![CDATA[").append(purchaseOrder.getBillingLine1Address()).append("]]></AddressLine>\n");
212            cxml.append("          <AddressLine label=\"Street2\" linenumber=\"2\"><![CDATA[").append(purchaseOrder.getBillingLine2Address()).append("]]></AddressLine>\n");
213            cxml.append("          <City><![CDATA[").append(purchaseOrder.getBillingCityName()).append("]]></City>\n"); // Required.
214            cxml.append("          <State>").append(purchaseOrder.getBillingStateCode()).append("</State>\n");
215            cxml.append("          <PostalCode>").append(purchaseOrder.getBillingPostalCode()).append("</PostalCode>\n"); // Required.
216            cxml.append("          <Country isocountrycode=\"").append(purchaseOrder.getBillingCountryCode()).append("\">").append(purchaseOrder.getBillingCountryCode()).append("</Country>\n");
217            cxml.append("        </Address>\n");
218            cxml.append("      </BillTo>\n");
219    
220            /** *** SHIP TO SECTION **** */
221            cxml.append("      <ShipTo>\n");
222            cxml.append("        <Address>\n");
223            cxml.append("          <TemplateName>Ship To</TemplateName>\n");
224            // AddressCode. A code to identify the address, that is sent to the supplier.
225            cxml.append("          <AddressCode>").append(purchaseOrder.getDeliveryCampusCode()).append(purchaseOrder.getOrganizationCode()).append("</AddressCode>\n");
226            cxml.append("          <Contact label=\"Name\" linenumber=\"1\"><![CDATA[").append(purchaseOrder.getDeliveryToName().trim()).append("]]></Contact>\n");
227            cxml.append("          <Contact label=\"PurchasingEmail\" linenumber=\"2\"><![CDATA[").append(contractManagerEmail).append("]]></Contact>\n");
228            if (ObjectUtils.isNotNull(purchaseOrder.getInstitutionContactEmailAddress())) {
229                cxml.append("          <Contact label=\"ContactEmail\" linenumber=\"3\"><![CDATA[").append(purchaseOrder.getInstitutionContactEmailAddress()).append("]]></Contact>\n");
230            }
231            else {
232                cxml.append("          <Contact label=\"ContactEmail\" linenumber=\"3\"><![CDATA[").append(purchaseOrder.getRequestorPersonEmailAddress()).append("]]></Contact>\n");
233            }
234            if (ObjectUtils.isNotNull(purchaseOrder.getInstitutionContactPhoneNumber())) {
235                cxml.append("          <Contact label=\"Phone\" linenumber=\"4\"><![CDATA[").append(purchaseOrder.getInstitutionContactPhoneNumber().trim()).append("]]></Contact>\n");
236            }
237            else {
238                cxml.append("          <Contact label=\"Phone\" linenumber=\"4\"><![CDATA[").append(purchaseOrder.getRequestorPersonPhoneNumber()).append("]]></Contact>\n");
239            }
240            
241            //check indicator to decide if receiving or delivery address should be sent to the vendor
242            if (purchaseOrder.getAddressToVendorIndicator()) {  //use receiving address
243                cxml.append("          <AddressLine label=\"Street1\" linenumber=\"1\"><![CDATA[").append(purchaseOrder.getReceivingName().trim()).append("]]></AddressLine>\n");
244                cxml.append("          <AddressLine label=\"Street2\" linenumber=\"2\"><![CDATA[").append(purchaseOrder.getReceivingLine1Address().trim()).append("]]></AddressLine>\n");
245                if (ObjectUtils.isNull(purchaseOrder.getReceivingLine2Address())) {
246                    cxml.append("          <AddressLine label=\"Street3\" linenumber=\"3\"><![CDATA[").append(" ").append("]]></AddressLine>\n");
247                }
248                else {
249                    cxml.append("          <AddressLine label=\"Street3\" linenumber=\"3\"><![CDATA[").append(purchaseOrder.getReceivingLine2Address()).append("]]></AddressLine>\n");
250                }
251                cxml.append("          <City><![CDATA[").append(purchaseOrder.getReceivingCityName().trim()).append("]]></City>\n");
252                cxml.append("          <State>").append(purchaseOrder.getReceivingStateCode()).append("</State>\n");
253                cxml.append("          <PostalCode>").append(purchaseOrder.getReceivingPostalCode()).append("</PostalCode>\n");
254                cxml.append("          <Country isocountrycode=\"").append(purchaseOrder.getReceivingCountryCode()).append("\">").append(purchaseOrder.getReceivingCountryCode()).append("</Country>\n");
255            }
256            else { //use final delivery address
257                if (StringUtils.isNotEmpty(purchaseOrder.getDeliveryBuildingName())) {
258                    cxml.append("          <Contact label=\"Building\" linenumber=\"5\"><![CDATA[").append(purchaseOrder.getDeliveryBuildingName()).append(" (").append(purchaseOrder.getDeliveryBuildingCode()).append(")]]></Contact>\n");
259                }
260                cxml.append("          <AddressLine label=\"Street1\" linenumber=\"1\"><![CDATA[").append(purchaseOrder.getDeliveryBuildingLine1Address().trim()).append("]]></AddressLine>\n");
261                cxml.append("          <AddressLine label=\"Street2\" linenumber=\"2\"><![CDATA[Room #").append(purchaseOrder.getDeliveryBuildingRoomNumber().trim()).append("]]></AddressLine>\n");
262                cxml.append("          <AddressLine label=\"Company\" linenumber=\"4\"><![CDATA[").append(purchaseOrder.getBillingName().trim()).append("]]></AddressLine>\n");
263                if (ObjectUtils.isNull(purchaseOrder.getDeliveryBuildingLine2Address())) {
264                    cxml.append("          <AddressLine label=\"Street3\" linenumber=\"3\"><![CDATA[").append(" ").append("]]></AddressLine>\n");
265                }
266                else {
267                    cxml.append("          <AddressLine label=\"Street3\" linenumber=\"3\"><![CDATA[").append(purchaseOrder.getDeliveryBuildingLine2Address()).append("]]></AddressLine>\n");
268                }
269                cxml.append("          <City><![CDATA[").append(purchaseOrder.getDeliveryCityName().trim()).append("]]></City>\n");
270                cxml.append("          <State>").append(purchaseOrder.getDeliveryStateCode()).append("</State>\n");
271                cxml.append("          <PostalCode>").append(purchaseOrder.getDeliveryPostalCode()).append("</PostalCode>\n");
272                cxml.append("          <Country isocountrycode=\"").append(purchaseOrder.getDeliveryCountryCode()).append("\">").append(purchaseOrder.getDeliveryCountryCode()).append("</Country>\n");
273            }
274    
275            cxml.append("        </Address>\n");
276            cxml.append("      </ShipTo>\n");
277            cxml.append("    </POHeader>\n");
278    
279            /** *** Items Section **** */
280            List detailList = purchaseOrder.getItems();
281            for (Iterator iter = detailList.iterator(); iter.hasNext();) {
282                PurchaseOrderItem poi = (PurchaseOrderItem) iter.next();
283                if ((ObjectUtils.isNotNull(poi.getItemType())) && poi.getItemType().isLineItemIndicator()) {
284                    cxml.append("    <POLine linenumber=\"").append(poi.getItemLineNumber()).append("\">\n");
285                    cxml.append("      <Item>\n");
286                    // CatalogNumber - This is a string that the supplier uses to identify the item (i.e., SKU). Optional.
287                    cxml.append("        <CatalogNumber><![CDATA[").append(poi.getItemCatalogNumber()).append("]]></CatalogNumber>\n");
288                    if (ObjectUtils.isNotNull(poi.getItemAuxiliaryPartIdentifier())) {
289                        cxml.append("        <AuxiliaryCatalogNumber><![CDATA[").append(poi.getItemAuxiliaryPartIdentifier()).append("]]></AuxiliaryCatalogNumber>\n");
290                    }
291                    cxml.append("        <Description><![CDATA[").append(poi.getItemDescription()).append("]]></Description>\n"); // Required.
292                    cxml.append("        <ProductUnitOfMeasure type=\"supplier\"><Measurement><MeasurementValue><![CDATA[").append(poi.getItemUnitOfMeasureCode()).append("]]></MeasurementValue></Measurement></ProductUnitOfMeasure>\n");
293                    cxml.append("        <ProductUnitOfMeasure type=\"system\"><Measurement><MeasurementValue><![CDATA[").append(poi.getItemUnitOfMeasureCode()).append("]]></MeasurementValue></Measurement></ProductUnitOfMeasure>\n");
294                    // ProductReferenceNumber - Unique id for hosted products in SelectSite
295                    if (poi.getExternalOrganizationB2bProductTypeName().equals("Punchout")) {
296                        cxml.append("        <ProductReferenceNumber>null</ProductReferenceNumber>\n");
297                    }
298                    else {
299                        cxml.append("        <ProductReferenceNumber>").append(poi.getExternalOrganizationB2bProductReferenceNumber()).append("</ProductReferenceNumber>\n");
300                    }
301                    // ProductType - Describes the type of the product or service. Valid values: Catalog, Form, Punchout. Mandatory.
302                    cxml.append("        <ProductType>").append(poi.getExternalOrganizationB2bProductTypeName()).append("</ProductType>\n");
303                    cxml.append("      </Item>\n");
304                    cxml.append("      <Quantity>").append(poi.getItemQuantity()).append("</Quantity>\n");
305                    // LineCharges - All the monetary charges for this line, including the price, tax, shipping, and handling.
306                    // Required.
307                    cxml.append("      <LineCharges>\n");
308                    cxml.append("        <UnitPrice>\n");
309                    cxml.append("          <Money currency=\"USD\">").append(poi.getItemUnitPrice()).append("</Money>\n");
310                    cxml.append("        </UnitPrice>\n");
311                    cxml.append("      </LineCharges>\n");
312                    cxml.append("    </POLine>\n");
313                }
314            }
315    
316            cxml.append("  </PurchaseOrder>\n");
317            cxml.append("</PurchaseOrderMessage>");
318    
319            LOG.debug("getCxml(): cXML for po number " + purchaseOrder.getPurapDocumentIdentifier() + ":\n" + cxml.toString());
320    
321            return cxml.toString();
322        }
323    
324        /**
325         * @see org.kuali.kfs.module.purap.document.service.B2BPurchaseOrderService#verifyCxmlPOData(org.kuali.kfs.module.purap.document.PurchaseOrderDocument,
326         *      org.kuali.rice.kim.bo.Person, java.lang.String, org.kuali.kfs.vnd.businessobject.ContractManager,
327         *      java.lang.String, java.lang.String)
328         */
329        public String verifyCxmlPOData(PurchaseOrderDocument purchaseOrder, String requisitionInitiatorId, String password, ContractManager contractManager, String contractManagerEmail, String vendorDuns) {
330            StringBuffer errors = new StringBuffer();
331    
332            if (ObjectUtils.isNull(purchaseOrder)) {
333                LOG.error("verifyCxmlPOData()  The Purchase Order is null.");
334                errors.append("Error occurred retrieving Purchase Order\n");
335                return errors.toString();
336            }
337            if (ObjectUtils.isNull(contractManager)) {
338                LOG.error("verifyCxmlPOData()  The contractManager is null.");
339                errors.append("Error occurred retrieving Contract Manager\n");
340                return errors.toString();
341            }
342            if (StringUtils.isEmpty(password)) {
343                LOG.error("verifyCxmlPOData()  The B2B PO password is required for the cXML PO but is missing.");
344                errors.append("Missing Data: B2B PO password\n");
345            }
346            if (ObjectUtils.isNull(purchaseOrder.getPurapDocumentIdentifier())) {
347                LOG.error("verifyCxmlPOData()  The purchase order Id is required for the cXML PO but is missing.");
348                errors.append("Missing Data: Purchase Order ID\n");
349            }
350            if (StringUtils.isEmpty(requisitionInitiatorId)) {
351                LOG.error("verifyCxmlPOData()  The requisition initiator Network Id is required for the cXML PO but is missing.");
352                errors.append("Missing Data: Requisition Initiator NetworkId\n");
353            }
354            if (ObjectUtils.isNull(purchaseOrder.getPurchaseOrderCreateTimestamp())) {
355                LOG.error("verifyCxmlPOData()  The PO create date is required for the cXML PO but is null.");
356                errors.append("Create Date\n");
357            }
358            if (StringUtils.isEmpty(contractManager.getContractManagerPhoneNumber())) {
359                LOG.error("verifyCxmlPOData()  The contract manager phone number is required for the cXML PO but is missing.");
360                errors.append("Missing Data: Contract Manager Phone Number\n");
361            }
362            if (StringUtils.isEmpty(contractManager.getContractManagerName())) {
363                LOG.error("verifyCxmlPOData()  The contract manager name is required for the cXML PO but is missing.");
364                errors.append("Missing Data: Contract Manager Name\n");
365            }
366            if (StringUtils.isEmpty(purchaseOrder.getDeliveryCampusCode())) {
367                LOG.error("verifyCxmlPOData()  The Delivery Campus Code is required for the cXML PO but is missing.");
368                errors.append("Missing Data: Delivery Campus Code\n");
369            }
370            if (StringUtils.isEmpty(purchaseOrder.getBillingName())) {
371                LOG.error("verifyCxmlPOData()  The Delivery Billing Name is required for the cXML PO but is missing.");
372                errors.append("Missing Data: Delivery Billing Name\n");
373            }
374            if (StringUtils.isEmpty(purchaseOrder.getBillingLine1Address())) {
375                LOG.error("verifyCxmlPOData()  The Billing Line 1 Address is required for the cXML PO but is missing.");
376                errors.append("Missing Data: Billing Line 1 Address\n");
377            }
378            if (StringUtils.isEmpty(purchaseOrder.getBillingLine2Address())) {
379                LOG.error("verifyCxmlPOData()  The Billing Line 2 Address is required for the cXML PO but is missing.");
380                errors.append("Missing Data: Billing Line 2 Address\n");
381            }
382            if (StringUtils.isEmpty(purchaseOrder.getBillingCityName())) {
383                LOG.error("verifyCxmlPOData()  The Billing Address City Name is required for the cXML PO but is missing.");
384                errors.append("Missing Data: Billing Address City Name\n");
385            }
386            if (StringUtils.isEmpty(purchaseOrder.getBillingStateCode())) {
387                LOG.error("verifyCxmlPOData()  The Billing Address State Code is required for the cXML PO but is missing.");
388                errors.append("Missing Data: Billing Address State Code\n");
389            }
390            if (StringUtils.isEmpty(purchaseOrder.getBillingPostalCode())) {
391                LOG.error("verifyCxmlPOData()  The Billing Address Postal Code is required for the cXML PO but is missing.");
392                errors.append("Missing Data: Billing Address Postal Code\n");
393            }
394            if (StringUtils.isEmpty(purchaseOrder.getDeliveryToName())) {
395                LOG.error("verifyCxmlPOData()  The Delivery To Name is required for the cXML PO but is missing.");
396                errors.append("Missing Data: Delivery To Name\n");
397            }
398            if (StringUtils.isEmpty(contractManagerEmail)) {
399                LOG.error("verifyCxmlPOData()  The Contract Manager Email is required for the cXML PO but is missing.");
400                errors.append("Missing Data: Contract Manager Email\n");
401            }
402            if (StringUtils.isEmpty(purchaseOrder.getRequestorPersonEmailAddress())) {
403                LOG.error("verifyCxmlPOData()  The Requesting Person Email Address is required for the cXML PO but is missing.");
404                errors.append("Missing Data: Requesting Person Email Address\n");
405            }
406            if (StringUtils.isEmpty(purchaseOrder.getRequestorPersonPhoneNumber())) {
407                LOG.error("verifyCxmlPOData()  The Requesting Person Phone Number is required for the cXML PO but is missing.");
408                errors.append("Missing Data: Requesting Person Phone Number\n");
409            }
410            if (StringUtils.isEmpty(purchaseOrder.getDeliveryBuildingLine1Address())) {
411                LOG.error("verifyCxmlPOData()  The Delivery Line 1 Address is required for the cXML PO but is missing.");
412                errors.append("Missing Data: Delivery Line 1 Address\n");
413            }
414            if (StringUtils.isEmpty(purchaseOrder.getDeliveryToName())) {
415                LOG.error("verifyCxmlPOData()  The Delivery To Name is required for the cXML PO but is missing.");
416                errors.append("Missing Data: Delivery To Name\n");
417            }
418            if (StringUtils.isEmpty(purchaseOrder.getDeliveryCityName())) {
419                LOG.error("verifyCxmlPOData()  The Delivery City Name is required for the cXML PO but is missing.");
420                errors.append("Missing Data: Delivery City Name\n");
421            }
422            if (StringUtils.isEmpty(purchaseOrder.getDeliveryStateCode())) {
423                LOG.error("verifyCxmlPOData()  The Delivery State is required for the cXML PO but is missing.");
424                errors.append("Missing Data: Delivery State\n");
425            }
426            if (StringUtils.isEmpty(purchaseOrder.getDeliveryPostalCode())) {
427                LOG.error("verifyCxmlPOData()  The Delivery Postal Code is required for the cXML PO but is missing.");
428                errors.append("Missing Data: Delivery Postal Code\n");
429            }
430    
431            // verify item data
432            List detailList = purchaseOrder.getItems();
433            for (Iterator iter = detailList.iterator(); iter.hasNext();) {
434                PurchaseOrderItem poi = (PurchaseOrderItem) iter.next();
435                if (ObjectUtils.isNotNull(poi.getItemType()) && poi.getItemType().isLineItemIndicator()) {
436                    if (ObjectUtils.isNull(poi.getItemLineNumber())) {
437                        LOG.error("verifyCxmlPOData()  The Item Line Number is required for the cXML PO but is missing.");
438                        errors.append("Missing Data: Item Line Number\n");
439                    }
440                    if (StringUtils.isEmpty(poi.getItemCatalogNumber())) {
441                        LOG.error("verifyCxmlPOData()  The Catalog Number for item number " + poi.getItemLineNumber() + " is required for the cXML PO but is missing.");
442                        errors.append("Missing Data: Item#" + poi.getItemLineNumber() + " - Catalog Number\n");
443                    }
444                    if (StringUtils.isEmpty(poi.getItemDescription())) {
445                        LOG.error("verifyCxmlPOData()  The Description for item number " + poi.getItemLineNumber() + " is required for the cXML PO but is missing.");
446                        errors.append("Missing Data: Item#" + poi.getItemLineNumber() + " - Description\n");
447                    }
448                    if (StringUtils.isEmpty(poi.getItemUnitOfMeasureCode())) {
449                        LOG.error("verifyCxmlPOData()  The Unit Of Measure Code for item number " + poi.getItemLineNumber() + " is required for the cXML PO but is missing.");
450                        errors.append("Missing Data: Item#" + poi.getItemLineNumber() + " - Unit Of Measure\n");
451                    }
452                    if (StringUtils.isEmpty(poi.getExternalOrganizationB2bProductTypeName())) {
453                        LOG.error("verifyCxmlPOData()  The External Org B2B Product Type Name for item number " + poi.getItemLineNumber() + " is required for the cXML PO but is missing.");
454                        errors.append("Missing Data: Item#" + poi.getItemLineNumber() + " - External Org B2B Product Type Name\n");
455                    }
456                    if (poi.getItemQuantity() == null) {
457                        LOG.error("verifyCxmlPOData()  The Order Quantity for item number " + poi.getItemLineNumber() + " is required for the cXML PO but is missing.");
458                        errors.append("Missing Data: Item#" + poi.getItemLineNumber() + " - Order Quantity\n");
459                    }
460                    if (poi.getItemUnitPrice() == null) {
461                        LOG.error("verifyCxmlPOData()  The Unit Price for item number " + poi.getItemLineNumber() + " is required for the cXML PO but is missing.");
462                        errors.append("Missing Data: Item#" + poi.getItemLineNumber() + " - Unit Price\n");
463                    }
464                }
465            } // end item looping
466    
467            return errors.toString();
468        } 
469    
470        /**
471         * Retrieve the Contract Manager's email
472         */
473        protected String getContractManagerEmail(ContractManager cm) {
474    
475            Person contractManager = getPersonService().getPerson(cm.getContractManagerUserIdentifier());
476            if (ObjectUtils.isNotNull(contractManager)) {
477                return contractManager.getEmailAddressUnmasked();
478            }
479            return "";
480        }
481    
482        public void setRequisitionService(RequisitionService requisitionService) {
483            this.requisitionService = requisitionService;
484        }
485    
486        public void setParameterService(ParameterService parameterService) {
487            this.parameterService = parameterService;
488        }
489    
490        public void setB2bDao(B2BDao b2bDao) {
491            this.b2bDao = b2bDao;
492        }
493    
494        /**
495         * @return Returns the personService.
496         */
497        protected PersonService<Person> getPersonService() {
498            if(personService==null)
499                personService = SpringContext.getBean(PersonService.class);
500            return personService;
501        }
502        
503        public void setB2bEnvironment(String environment) {
504            b2bEnvironment = environment;
505        }
506    
507        public void setB2bUserAgent(String userAgent) {
508            b2bUserAgent = userAgent;
509        }
510    
511        public void setB2bPurchaseOrderURL(String purchaseOrderURL) {
512            b2bPurchaseOrderURL = purchaseOrderURL;
513        }
514        
515        public void setB2bPurchaseOrderIdentity(String b2bPurchaseOrderIdentity) {
516            this.b2bPurchaseOrderIdentity = b2bPurchaseOrderIdentity;
517        }
518    
519        public void setB2bPurchaseOrderPassword(String purchaseOrderPassword) {
520            b2bPurchaseOrderPassword = purchaseOrderPassword;
521        }
522    
523    }