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.cam.batch.service.impl;
017    
018    import java.io.BufferedReader;
019    import java.io.File;
020    import java.io.FileNotFoundException;
021    import java.io.FileReader;
022    import java.io.IOException;
023    import java.sql.Timestamp;
024    import java.text.SimpleDateFormat;
025    import java.util.ArrayList;
026    import java.util.HashMap;
027    import java.util.List;
028    import java.util.Locale;
029    import java.util.Map;
030    
031    import org.apache.commons.lang.StringUtils;
032    import org.kuali.kfs.module.cam.CamsConstants;
033    import org.kuali.kfs.module.cam.CamsPropertyConstants;
034    import org.kuali.kfs.module.cam.batch.service.AssetBarcodeInventoryLoadService;
035    import org.kuali.kfs.module.cam.businessobject.Asset;
036    import org.kuali.kfs.module.cam.businessobject.BarcodeInventoryErrorDetail;
037    import org.kuali.kfs.module.cam.document.BarcodeInventoryErrorDocument;
038    import org.kuali.kfs.module.cam.document.validation.event.ValidateBarcodeInventoryEvent;
039    import org.kuali.kfs.module.cam.document.web.struts.AssetBarCodeInventoryInputFileForm;
040    import org.kuali.kfs.sys.KFSConstants;
041    import org.kuali.kfs.sys.KFSKeyConstants;
042    import org.kuali.rice.kew.exception.WorkflowException;
043    import org.kuali.rice.kew.util.KEWConstants;
044    import org.kuali.rice.kns.bo.AdHocRoutePerson;
045    import org.kuali.rice.kns.bo.AdHocRouteRecipient;
046    import org.kuali.rice.kns.document.Document;
047    import org.kuali.rice.kns.service.BusinessObjectService;
048    import org.kuali.rice.kns.service.DataDictionaryService;
049    import org.kuali.rice.kns.service.DateTimeService;
050    import org.kuali.rice.kns.service.DocumentService;
051    import org.kuali.rice.kns.service.KualiRuleService;
052    import org.kuali.rice.kns.service.ParameterService;
053    import org.kuali.rice.kns.util.GlobalVariables;
054    import org.kuali.rice.kns.util.KualiDecimal;
055    import org.kuali.rice.kns.workflow.service.WorkflowDocumentService;
056    
057    /**
058     * Implementation of the AssetBarcodeInventoryLoadService interface. Handles loading, parsing, and storing of incoming barcode
059     * inventory files.
060     */
061    public class AssetBarcodeInventoryLoadServiceImpl implements AssetBarcodeInventoryLoadService {
062        private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AssetBarcodeInventoryLoadServiceImpl.class);
063    
064        public static final String MESSAGE_NO_DOCUMENT_CREATED = "NO barcode inventory error document was created.";
065        public static final String DOCUMENTS_MSG = "The following barcode inventory error document were created";
066        public static final String TOTAL_RECORDS_UPLOADED_MSG = "Total records uploaded";
067        public static final String TOTAL_RECORDS_IN_ERROR_MSG = "Total records in error";
068    
069        protected static final int MAX_NUMBER_OF_COLUMNS = 8;
070        protected static final String DOCUMENT_EXPLANATION = "BARCODE ERROR INVENTORY";
071    
072        private BusinessObjectService businessObjectService;
073        private WorkflowDocumentService workflowDocumentService;
074        private DataDictionaryService dataDictionaryService;
075        private KualiRuleService kualiRuleService;
076        private DocumentService documentService;
077        private ParameterService parameterService;
078        private DateTimeService dateTimeService;
079    
080        /**
081         * Determines whether or not the BCIE document has all its records corrected or deleted
082         * 
083         * @param document
084         * @return boolean
085         */
086        public boolean isFullyProcessed(Document document) {
087            BarcodeInventoryErrorDocument barcodeInventoryErrorDocument = (BarcodeInventoryErrorDocument)document;
088            boolean result = true;
089            List<BarcodeInventoryErrorDetail> barcodeInventoryErrorDetails = barcodeInventoryErrorDocument.getBarcodeInventoryErrorDetail();
090            BarcodeInventoryErrorDetail barcodeInventoryErrorDetail;
091    
092            for (BarcodeInventoryErrorDetail detail : barcodeInventoryErrorDetails) {
093                if (detail.getErrorCorrectionStatusCode().equals(CamsConstants.BarCodeInventoryError.STATUS_CODE_ERROR)) {
094                    result = false;
095                    break;
096                }
097            }
098            return result;
099        }
100    
101        /**
102         * @see org.kuali.kfs.module.cam.batch.service.AssetBarcodeInventoryLoadService#isCurrentUserInitiator(org.kuali.rice.kns.document.Document)
103         */
104        public boolean isCurrentUserInitiator(Document document) {
105            if (document != null) {
106                return GlobalVariables.getUserSession().getPerson().getPrincipalId().equalsIgnoreCase(document.getDocumentHeader().getWorkflowDocument().getInitiatorPrincipalId());
107            }
108            return false;
109        }
110    
111        /**
112         * @see org.kuali.module.cams.service.AssetBarcodeInventoryLoadService#isFileFormatValid(java.io.File)
113         */
114        public boolean isFileFormatValid(File file) {
115            LOG.debug("isFileFormatValid(File file) - start");
116            String fileName = file.getName();
117    
118            BufferedReader input = null;
119    
120            // Getting the length of each field that needs to be validated
121            Integer campusTagNumberMaxLength = dataDictionaryService.getAttributeMaxLength(Asset.class, CamsPropertyConstants.Asset.CAMPUS_TAG_NUMBER);
122            Integer inventoryScannedCodeMaxLength = new Integer(1);
123            Integer InventoryDateMaxLength = dataDictionaryService.getAttributeMaxLength(BarcodeInventoryErrorDetail.class, CamsPropertyConstants.BarcodeInventory.INVENTORY_DATE);
124            Integer campusCodeMaxLength = dataDictionaryService.getAttributeMaxLength(Asset.class, CamsPropertyConstants.Asset.CAMPUS_CODE);
125            Integer buildingCodeMaxLength = dataDictionaryService.getAttributeMaxLength(Asset.class, CamsPropertyConstants.Asset.BUILDING_CODE);
126            Integer buildingRoomNumberMaxLength = dataDictionaryService.getAttributeMaxLength(Asset.class, CamsPropertyConstants.Asset.BUILDING_ROOM_NUMBER);
127            Integer buildingSubRoomNumberMaxLength = dataDictionaryService.getAttributeMaxLength(Asset.class, CamsPropertyConstants.Asset.BUILDING_SUB_ROOM_NUMBER);
128            Integer conditionCodeMaxLength = dataDictionaryService.getAttributeMaxLength(Asset.class, CamsPropertyConstants.Asset.CONDITION_CODE);
129    
130            // Getting the label of each field from data dictionary.
131            String campusTagNumberLabel = dataDictionaryService.getAttributeLabel(Asset.class, CamsPropertyConstants.Asset.CAMPUS_TAG_NUMBER);
132            String inventoryScannedCodeLabel = dataDictionaryService.getAttributeLabel(Asset.class, CamsPropertyConstants.BarcodeInventory.UPLOAD_SCAN_INDICATOR);
133            String InventoryDateLabel = dataDictionaryService.getAttributeLabel(BarcodeInventoryErrorDetail.class, CamsPropertyConstants.BarcodeInventory.INVENTORY_DATE);
134            String campusCodeLabel = dataDictionaryService.getAttributeLabel(Asset.class, CamsPropertyConstants.Asset.CAMPUS_CODE);
135            String buildingCodeLabel = dataDictionaryService.getAttributeLabel(Asset.class, CamsPropertyConstants.Asset.BUILDING_CODE);
136            String buildingRoomNumberLabel = dataDictionaryService.getAttributeLabel(Asset.class, CamsPropertyConstants.Asset.BUILDING_ROOM_NUMBER);
137            String buildingSubRoomNumberLabel = dataDictionaryService.getAttributeLabel(Asset.class, CamsPropertyConstants.Asset.BUILDING_SUB_ROOM_NUMBER);
138            String conditionCodeLabel = dataDictionaryService.getAttributeLabel(Asset.class, CamsPropertyConstants.Asset.CONDITION_CODE);
139    
140            try {
141                int recordCount = 0;
142                String errorMsg = "";
143                String errorMessage = "";
144                boolean proceed = true;
145                String lengthError = "exceeds maximum length";
146    
147                input = new BufferedReader(new FileReader(file));
148                String line = null;
149    
150    
151                while ((line = input.readLine()) != null) {
152                    recordCount++;
153                    errorMsg = "";
154                    line = StringUtils.remove(line, "\"");
155    
156                    String[] column = org.springframework.util.StringUtils.delimitedListToStringArray(line, ",");
157    
158                    if (MAX_NUMBER_OF_COLUMNS < column.length) {
159                        // Error more columns that allowed. put it in the constants class.
160                        errorMsg += "  Barcode inventory file has record(s) with more than " + MAX_NUMBER_OF_COLUMNS + " columns\n";
161                        proceed = false;
162                    }
163                    else if (MAX_NUMBER_OF_COLUMNS > column.length) {
164                        errorMsg += "  Barcode inventory file has record(s) with less than " + MAX_NUMBER_OF_COLUMNS + " columns\n";
165                        proceed = false;
166                    }
167                    else {
168    
169                        // Validating length of each field
170                        if (column[0].length() > campusTagNumberMaxLength.intValue()) {
171                            errorMsg += ", " + campusTagNumberLabel;
172                        }
173    
174                        if (column[1].length() > inventoryScannedCodeMaxLength.intValue()) {
175                            errorMsg += ", " + inventoryScannedCodeLabel;
176                        }
177    
178                        if (column[2].length() > InventoryDateMaxLength.intValue()) {
179                            errorMsg += ", " + InventoryDateLabel;
180                        }
181    
182                        if (column[3].length() > campusCodeMaxLength.intValue()) {
183                            errorMsg += ", " + campusCodeLabel;
184                        }
185                        if (column[4].length() > buildingCodeMaxLength.intValue()) {
186                            errorMsg += ", " + buildingCodeLabel;
187                        }
188                        if (column[5].length() > buildingRoomNumberMaxLength.intValue()) {
189                            errorMsg += ", " + buildingRoomNumberLabel;
190                        }
191                        if (column[6].length() > buildingSubRoomNumberMaxLength.intValue()) {
192                            errorMsg += ", " + buildingSubRoomNumberLabel;
193                        }
194                        if (column[7].length() > conditionCodeMaxLength.intValue()) {
195                            errorMsg += ", " + conditionCodeLabel;
196                        }
197    
198                        if (!StringUtils.isBlank(errorMsg)) {
199                            errorMsg += " " + lengthError;
200                        }
201    
202                        // Validating other than the length of the fields
203                        if (!column[1].equals(CamsConstants.BarCodeInventory.BCI_SCANED_INTO_DEVICE) && !column[1].equals(CamsConstants.BarCodeInventory.BCI_MANUALLY_KEYED_CODE)) {
204                            errorMsg += ", " + inventoryScannedCodeLabel + " is invalid";
205                        }
206                    }
207                    if (!StringUtils.isBlank(errorMsg)) {
208                        errorMsg = "Error on record number " + recordCount + ": " + errorMsg.substring(2) + "\n";
209                        GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, KFSKeyConstants.ERROR_CUSTOM, errorMsg);
210                        errorMessage += errorMsg;
211                        LOG.error(errorMsg);
212                    }
213                    if (!proceed)
214                        break;
215                }
216                if (!StringUtils.isBlank(errorMessage)) {
217                    return false;
218                }
219    
220                return true;
221            }
222            catch (FileNotFoundException e1) {
223                LOG.error("file to parse not found " + fileName, e1);
224                throw new RuntimeException("Cannot find the file requested to be parsed " + fileName + " " + e1.getMessage(), e1);
225            }
226            catch (Exception e) {
227                LOG.error("Error running file validation - File: " + fileName, e);
228                throw new IllegalArgumentException("Error running file validation - File: " + fileName);
229            }
230            finally {
231                LOG.debug("isFileFormatValid(File file) - end");
232                try {
233                    if (input != null) {
234                        input.close();
235                    }
236                }
237                catch (IOException ex) {
238                    LOG.error("isFileFormatValid() error closing file.", ex);
239                }
240            }
241    
242        }
243    
244        /**
245         * @see org.kuali.module.cams.service.AssetBarCodeInventoryLoadService#processFile(java.io.File)
246         */
247        public boolean processFile(File file, AssetBarCodeInventoryInputFileForm form) {
248            LOG.debug("processFile(File file) - start");
249    
250            BufferedReader input = null;
251            String fileName = file.getName();
252    
253            String day;
254            String month;
255            String year;
256            String hours;
257            String minutes;
258            String seconds;
259            boolean isValid = true;
260    
261            SimpleDateFormat formatter = new SimpleDateFormat(CamsConstants.DateFormats.MONTH_DAY_YEAR + " " + CamsConstants.DateFormats.STANDARD_TIME, Locale.US);
262            formatter.setLenient(false);
263            
264            BarcodeInventoryErrorDetail barcodeInventoryErrorDetail;
265            List<BarcodeInventoryErrorDetail> barcodeInventoryErrorDetails = new ArrayList<BarcodeInventoryErrorDetail>();
266    
267            List<BarcodeInventoryErrorDocument> barcodeInventoryErrorDocuments = new ArrayList<BarcodeInventoryErrorDocument>();
268            try {
269                Long ln = new Long(1);
270                input = new BufferedReader(new FileReader(file));
271                String line = null;
272    
273                while ((line = input.readLine()) != null) {
274                    line = StringUtils.remove(line, "\"");
275                    String[] lineStrings = org.springframework.util.StringUtils.delimitedListToStringArray(line, ",");
276    
277                    // Parsing date so it can be validated.
278                    lineStrings[2] = StringUtils.rightPad(lineStrings[2].trim(), 14, "0");
279    
280                    day = lineStrings[2].substring(0, 2);
281                    month = lineStrings[2].substring(2, 4);
282                    year = lineStrings[2].substring(4, 8);
283                    hours = lineStrings[2].substring(8, 10);
284                    minutes = lineStrings[2].substring(10, 12);
285                    seconds = lineStrings[2].substring(12);
286    
287                    String stringDate = month + "/" + day + "/" + year + " " + hours + ":" + minutes + ":" + seconds;
288                    Timestamp timestamp = null;
289    
290                    // If date has invalid format set its value to null
291                    try {
292                        timestamp = new Timestamp(formatter.parse(stringDate).getTime());
293                    }
294                    catch (Exception e) {
295                    }
296    
297                    // Its set to null because for some reason java parses "00000000000000" as 0002-11-30
298                    if (lineStrings[2].equals(StringUtils.repeat("0", 14))) {
299                        timestamp = null;
300                    }
301                    
302                    barcodeInventoryErrorDetail = new BarcodeInventoryErrorDetail();
303                    barcodeInventoryErrorDetail.setUploadRowNumber(ln);
304                    barcodeInventoryErrorDetail.setAssetTagNumber(lineStrings[0].trim());
305                    barcodeInventoryErrorDetail.setUploadScanIndicator(lineStrings[1].equals(CamsConstants.BarCodeInventory.BCI_SCANED_INTO_DEVICE));
306                    barcodeInventoryErrorDetail.setUploadScanTimestamp(timestamp);
307                    barcodeInventoryErrorDetail.setCampusCode(lineStrings[3].trim().toUpperCase());
308                    barcodeInventoryErrorDetail.setBuildingCode(lineStrings[4].trim().toUpperCase());
309                    barcodeInventoryErrorDetail.setBuildingRoomNumber(lineStrings[5].trim().toUpperCase());
310                    barcodeInventoryErrorDetail.setBuildingSubRoomNumber(lineStrings[6].trim().toUpperCase());
311                    barcodeInventoryErrorDetail.setAssetConditionCode(lineStrings[7].trim().toUpperCase());
312                    barcodeInventoryErrorDetail.setErrorCorrectionStatusCode(CamsConstants.BarCodeInventoryError.STATUS_CODE_ERROR);
313                    barcodeInventoryErrorDetail.setCorrectorUniversalIdentifier(GlobalVariables.getUserSession().getPerson().getPrincipalId());
314    
315                    barcodeInventoryErrorDetails.add(barcodeInventoryErrorDetail);
316                    ln++;
317                }
318                processBarcodeInventory(barcodeInventoryErrorDetails, form);
319    
320                // Removing *.done files that are created automatically by the framework.
321                this.removeDoneFile(file);
322    
323                return true;
324            }
325            catch (FileNotFoundException e1) {
326                LOG.error("file to parse not found " + fileName, e1);
327                throw new RuntimeException("Cannot find the file requested to be parsed " + fileName + " " + e1.getMessage(), e1);
328            }
329            catch (Exception ex) {
330                LOG.error("Error reading file", ex);
331                throw new IllegalArgumentException("Error reading file: " + ex.getMessage(), ex);
332            }
333            finally {
334                LOG.debug("processFile(File file) - End");
335    
336                try {
337                    if (input != null) {
338                        input.close();
339                    }
340                }
341                catch (IOException ex) {
342                    LOG.error("loadFlatFile() error closing file.", ex);
343                }
344            }
345        }
346    
347        /**
348         * This method removes the *.done files. If not deleted, then the program will display the name of the file in a puldown menu
349         * with a label of ready for process.
350         * 
351         * @param file
352         */
353        protected void removeDoneFile(File file) {
354            String filePath = file.getAbsolutePath();
355            File doneFile = new File(StringUtils.substringBeforeLast(filePath, ".") + ".done");
356    
357            if (doneFile.exists()) {
358                doneFile.delete();
359            }
360        }
361    
362        /**
363         * This method invokes the rules in order to validate each records of the barcode file and invokes the method that updates the
364         * asset table with the records that passes the rules validation
365         * 
366         * @param barcodeInventoryErrorDetails
367         */
368        protected void processBarcodeInventory(List<BarcodeInventoryErrorDetail> barcodeInventoryErrorDetails, AssetBarCodeInventoryInputFileForm form) throws Exception {
369            Long lineNumber = new Long(0);
370            boolean docCreated = false;
371            int errorRecCount = 0;
372            int totalRecCount = 0;
373    
374            BarcodeInventoryErrorDocument barcodeInventoryErrorDocument = createInvalidBarcodeInventoryDocument(barcodeInventoryErrorDetails, form.getUploadDescription());
375            // apply rules for the new cash control detail
376            kualiRuleService.applyRules(new ValidateBarcodeInventoryEvent("", barcodeInventoryErrorDocument, true));
377    
378            List<BarcodeInventoryErrorDetail> tmpBarcodeInventoryErrorDetails = new ArrayList<BarcodeInventoryErrorDetail>();
379    
380            for (BarcodeInventoryErrorDetail barcodeInventoryErrorDetail : barcodeInventoryErrorDetails) {
381                totalRecCount++;
382                // if no error found, then update asset table.
383                if (!barcodeInventoryErrorDetail.getErrorCorrectionStatusCode().equals(CamsConstants.BarCodeInventoryError.STATUS_CODE_ERROR)) {
384                    this.updateAssetInformation(barcodeInventoryErrorDetail,true);
385                }
386                else {
387                    errorRecCount++;
388                    lineNumber++;
389                    // Assigning the row number to each invalid BCIE record
390                    barcodeInventoryErrorDetail.setUploadRowNumber(lineNumber);
391    
392                    // Storing in temp collection the invalid BCIE records.
393                    tmpBarcodeInventoryErrorDetails.add(barcodeInventoryErrorDetail);
394                }
395            }
396            // *********************************************************************
397            // Storing the invalid barcode inventory records.
398            // *********************************************************************
399            String documentsCreated = "";
400            if (!tmpBarcodeInventoryErrorDetails.isEmpty()) {
401                documentsCreated = this.createBarcodeInventoryErrorDocuments(tmpBarcodeInventoryErrorDetails, barcodeInventoryErrorDocument, form);
402                docCreated = true;
403            }
404    
405            if (!docCreated) {
406                form.getMessages().add(MESSAGE_NO_DOCUMENT_CREATED);
407            }
408            else {
409                // Adding the list of documents that were created in the message list
410                form.getMessages().add(DOCUMENTS_MSG + ": " + documentsCreated.substring(2));
411            }
412            form.getMessages().add(TOTAL_RECORDS_UPLOADED_MSG + ": " + StringUtils.rightPad(Integer.toString(totalRecCount), 5, " "));
413            form.getMessages().add(TOTAL_RECORDS_IN_ERROR_MSG + ": " + StringUtils.rightPad(Integer.toString(errorRecCount), 5, " "));
414        }
415    
416    
417        /**
418         * This method...
419         * 
420         * @param bcies
421         * @param barcodeInventoryErrorDocument
422         */
423        protected String createBarcodeInventoryErrorDocuments(List<BarcodeInventoryErrorDetail> bcies, BarcodeInventoryErrorDocument barcodeInventoryErrorDocument, AssetBarCodeInventoryInputFileForm form) {
424            List<BarcodeInventoryErrorDetail> barcodeInventoryErrorDetails = new ArrayList<BarcodeInventoryErrorDetail>();
425            boolean isFirstDocument = true;
426            int ln = 0;
427            int bcieCount = 0;
428            String documentsCreated = "";
429            int maxNumberRecordsPerDocument = 300;
430    
431            try {
432                if (parameterService.parameterExists(BarcodeInventoryErrorDocument.class, CamsConstants.Parameters.MAX_NUMBER_OF_RECORDS_PER_DOCUMENT)) {
433                    maxNumberRecordsPerDocument = new Integer(parameterService.getParameterValue(BarcodeInventoryErrorDocument.class, CamsConstants.Parameters.MAX_NUMBER_OF_RECORDS_PER_DOCUMENT)).intValue();
434                }
435    
436                while (true) {
437                    if ((ln > maxNumberRecordsPerDocument) || (bcieCount >= bcies.size())) {
438                        // This if was added in order to not waste the document already created and not create a new one.
439                        if (!isFirstDocument) {
440                            barcodeInventoryErrorDocument = createInvalidBarcodeInventoryDocument(barcodeInventoryErrorDetails, form.getUploadDescription());
441                        }
442                        documentsCreated += ", " + barcodeInventoryErrorDocument.getDocumentNumber();
443    
444                        barcodeInventoryErrorDocument.setBarcodeInventoryErrorDetail(barcodeInventoryErrorDetails);
445                        saveInvalidBarcodeInventoryDocument(barcodeInventoryErrorDocument);
446    
447                        barcodeInventoryErrorDetails = new ArrayList<BarcodeInventoryErrorDetail>();
448    
449                        if (bcieCount >= bcies.size())
450                            break;
451    
452                        ln = 0;
453                        isFirstDocument = false;
454                    }
455                    
456                    BarcodeInventoryErrorDetail barcodeInventoryErrorDetail =bcies.get(bcieCount);
457                    barcodeInventoryErrorDetail.setUploadRowNumber(Long.valueOf(ln+1));
458                    barcodeInventoryErrorDetails.add(barcodeInventoryErrorDetail);
459                    
460                    ln++;
461                    bcieCount++;
462                }
463            }
464            catch (Exception e) {
465                LOG.error("Error creating BCIE documents", e);
466                throw new IllegalArgumentException("Error creating BCIE documents: " + e.getMessage(), e);
467            }
468            return documentsCreated;
469        }
470    
471    
472        /**
473         * This method updates the asset information particularly the building code, bulding room, building subrool, campus code, and
474         * condition code
475         * 
476         * @param barcodeInventoryErrorDetail
477         */
478        public void updateAssetInformation(BarcodeInventoryErrorDetail barcodeInventoryErrorDetail, boolean updateWithDateAssetWasScanned) {
479            Map<String, String> fieldValues = new HashMap<String, String>();
480            fieldValues.put(CamsPropertyConstants.Asset.CAMPUS_TAG_NUMBER, barcodeInventoryErrorDetail.getAssetTagNumber());
481            Asset asset = ((List<Asset>) businessObjectService.findMatching(Asset.class, fieldValues)).get(0);
482    
483            asset.setInventoryScannedCode((barcodeInventoryErrorDetail.isUploadScanIndicator() ? CamsConstants.BarCodeInventory.BCI_SCANED_INTO_DEVICE : CamsConstants.BarCodeInventory.BCI_MANUALLY_KEYED_CODE));
484            asset.setBuildingCode(barcodeInventoryErrorDetail.getBuildingCode());
485            asset.setBuildingRoomNumber(barcodeInventoryErrorDetail.getBuildingRoomNumber());
486            asset.setBuildingSubRoomNumber(barcodeInventoryErrorDetail.getBuildingSubRoomNumber());
487            asset.setCampusCode(barcodeInventoryErrorDetail.getCampusCode());
488            asset.setConditionCode(barcodeInventoryErrorDetail.getAssetConditionCode());        
489           
490            if (updateWithDateAssetWasScanned) {
491                asset.setLastInventoryDate(barcodeInventoryErrorDetail.getUploadScanTimestamp());
492            } else {
493                asset.setLastInventoryDate(new Timestamp(this.dateTimeService.getCurrentSqlDate().getTime()));
494            }
495            
496            // Updating asset information
497            businessObjectService.save(asset);
498        }
499    
500        /**
501         * This method creates a transaction document with the invalid barcode inventory records
502         * 
503         * @param barcodeInventoryErrorDetails
504         * @return BarcodeInventoryErrorDocument
505         */
506        protected BarcodeInventoryErrorDocument createInvalidBarcodeInventoryDocument(List<BarcodeInventoryErrorDetail> barcodeInventoryErrorDetails, String uploadDescription) throws WorkflowException {
507            BarcodeInventoryErrorDocument document = (BarcodeInventoryErrorDocument) documentService.getNewDocument(BarcodeInventoryErrorDocument.class);
508    
509            document.getDocumentHeader().setExplanation(DOCUMENT_EXPLANATION);
510            document.getDocumentHeader().setFinancialDocumentTotalAmount(new KualiDecimal(0));
511            document.getDocumentHeader().setDocumentDescription(uploadDescription);
512            document.setUploaderUniversalIdentifier(GlobalVariables.getUserSession().getPerson().getPrincipalId());
513            document.setBarcodeInventoryErrorDetail(barcodeInventoryErrorDetails);
514    
515            return document;
516        }
517    
518    
519        /**
520         * saves the barcode inventory document
521         * 
522         * @param document
523         */
524        protected void saveInvalidBarcodeInventoryDocument(BarcodeInventoryErrorDocument document) {
525            try {
526                // The errors are being deleted because, when the document services finds any error then, changes are not saved.
527                GlobalVariables.clear();
528    
529                // no adhoc recipient need to add when submit doc. doc will route to the doc uploader, i.e. initiator automtically.
530                List<AdHocRouteRecipient> adHocRouteRecipients = new ArrayList<AdHocRouteRecipient>();
531                documentService.routeDocument(document, "Routed Update Barcode Inventory Document", adHocRouteRecipients);
532            }
533            catch (Exception e) {
534                LOG.error("Error persisting document # " + document.getDocumentHeader().getDocumentNumber() + " " + e.getMessage(), e);
535                throw new RuntimeException("Error persisting document # " + document.getDocumentHeader().getDocumentNumber() + " " + e.getMessage(), e);
536            }
537        }
538    
539        /**
540         * This method builds a recipient for Approval.
541         * 
542         * @param userId
543         * @return
544         */
545        protected AdHocRouteRecipient buildApprovePersonRecipient(String userId) {
546            AdHocRouteRecipient adHocRouteRecipient = new AdHocRoutePerson();
547            adHocRouteRecipient.setActionRequested(KEWConstants.ACTION_REQUEST_APPROVE_REQ);
548            adHocRouteRecipient.setId(userId);
549            return adHocRouteRecipient;
550        }
551    
552        public void setBusinessObjectService(BusinessObjectService businessObjectService) {
553            this.businessObjectService = businessObjectService;
554        }
555    
556        public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
557            this.dataDictionaryService = dataDictionaryService;
558        }
559    
560        public void setWorkflowDocumentService(WorkflowDocumentService workflowDocumentService) {
561            this.workflowDocumentService = workflowDocumentService;
562        }
563    
564        public void setKualiRuleService(KualiRuleService ruleService) {
565            this.kualiRuleService = ruleService;
566        }
567    
568        public void setDocumentService(DocumentService documentService) {
569            this.documentService = documentService;
570        }
571    
572        public ParameterService getParameterService() {
573            return parameterService;
574        }
575    
576        public void setParameterService(ParameterService parameterService) {
577            this.parameterService = parameterService;
578        }
579    
580        public void setDateTimeService(DateTimeService dateTimeService) {
581            this.dateTimeService = dateTimeService;
582        }
583    }