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