View Javadoc
1   /*
2    * The Kuali Financial System, a comprehensive financial management system for higher education.
3    * 
4    * Copyright 2005-2014 The Kuali Foundation
5    * 
6    * This program is free software: you can redistribute it and/or modify
7    * it under the terms of the GNU Affero General Public License as
8    * published by the Free Software Foundation, either version 3 of the
9    * License, or (at your option) any later version.
10   * 
11   * This program is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   * GNU Affero General Public License for more details.
15   * 
16   * You should have received a copy of the GNU Affero General Public License
17   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18   */
19  package org.kuali.kfs.module.cam.batch.service.impl;
20  
21  import java.io.BufferedReader;
22  import java.io.File;
23  import java.io.FileNotFoundException;
24  import java.io.FileReader;
25  import java.io.IOException;
26  import java.sql.Timestamp;
27  import java.text.SimpleDateFormat;
28  import java.util.ArrayList;
29  import java.util.HashMap;
30  import java.util.List;
31  import java.util.Locale;
32  import java.util.Map;
33  
34  import org.apache.commons.lang.StringUtils;
35  import org.kuali.kfs.gl.service.impl.StringHelper;
36  import org.kuali.kfs.module.cam.CamsConstants;
37  import org.kuali.kfs.module.cam.CamsPropertyConstants;
38  import org.kuali.kfs.module.cam.batch.service.AssetBarcodeInventoryLoadService;
39  import org.kuali.kfs.module.cam.businessobject.Asset;
40  import org.kuali.kfs.module.cam.businessobject.AssetLocation;
41  import org.kuali.kfs.module.cam.businessobject.BarcodeInventoryErrorDetail;
42  import org.kuali.kfs.module.cam.document.BarcodeInventoryErrorDocument;
43  import org.kuali.kfs.module.cam.document.validation.event.ValidateBarcodeInventoryEvent;
44  import org.kuali.kfs.module.cam.document.web.struts.AssetBarCodeInventoryInputFileForm;
45  import org.kuali.kfs.sys.KFSConstants;
46  import org.kuali.kfs.sys.KFSKeyConstants;
47  import org.kuali.rice.core.api.datetime.DateTimeService;
48  import org.kuali.rice.core.api.util.type.KualiDecimal;
49  import org.kuali.rice.coreservice.framework.parameter.ParameterService;
50  import org.kuali.rice.kew.api.KewApiConstants;
51  import org.kuali.rice.kew.api.document.WorkflowDocumentService;
52  import org.kuali.rice.kew.api.exception.WorkflowException;
53  import org.kuali.rice.kns.service.DataDictionaryService;
54  import org.kuali.rice.krad.bo.AdHocRoutePerson;
55  import org.kuali.rice.krad.bo.AdHocRouteRecipient;
56  import org.kuali.rice.krad.document.Document;
57  import org.kuali.rice.krad.service.BusinessObjectService;
58  import org.kuali.rice.krad.service.DocumentService;
59  import org.kuali.rice.krad.service.KualiRuleService;
60  import org.kuali.rice.krad.util.GlobalVariables;
61  
62  /**
63   * Implementation of the AssetBarcodeInventoryLoadService interface. Handles loading, parsing, and storing of incoming barcode
64   * inventory files.
65   */
66  public class AssetBarcodeInventoryLoadServiceImpl implements AssetBarcodeInventoryLoadService {
67      private static org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(AssetBarcodeInventoryLoadServiceImpl.class);
68  
69      public static final String MESSAGE_NO_DOCUMENT_CREATED = "NO barcode inventory error document was created.";
70      public static final String DOCUMENTS_MSG = "The following barcode inventory error document were created";
71      public static final String TOTAL_RECORDS_UPLOADED_MSG = "Total records uploaded";
72      public static final String TOTAL_RECORDS_IN_ERROR_MSG = "Total records in error";
73  
74      protected static final int MAX_NUMBER_OF_COLUMNS = 8;
75      protected static final String DOCUMENT_EXPLANATION = "BARCODE ERROR INVENTORY";
76  
77      private BusinessObjectService businessObjectService;
78      private WorkflowDocumentService workflowDocumentService;
79      private DataDictionaryService dataDictionaryService;
80      private KualiRuleService kualiRuleService;
81      private DocumentService documentService;
82      private ParameterService parameterService;
83      private DateTimeService dateTimeService;
84  
85      /**
86       * Determines whether or not the BCIE document has all its records corrected or deleted
87       * 
88       * @param document
89       * @return boolean
90       */
91      public boolean isFullyProcessed(Document document) {
92          BarcodeInventoryErrorDocument barcodeInventoryErrorDocument = (BarcodeInventoryErrorDocument)document;
93          boolean result = true;
94          List<BarcodeInventoryErrorDetail> barcodeInventoryErrorDetails = barcodeInventoryErrorDocument.getBarcodeInventoryErrorDetail();
95          BarcodeInventoryErrorDetail barcodeInventoryErrorDetail;
96  
97          for (BarcodeInventoryErrorDetail detail : barcodeInventoryErrorDetails) {
98              if (detail.getErrorCorrectionStatusCode().equals(CamsConstants.BarCodeInventoryError.STATUS_CODE_ERROR)) {
99                  result = false;
100                 break;
101             }
102         }
103         return result;
104     }
105 
106     /**
107      * @see org.kuali.kfs.module.cam.batch.service.AssetBarcodeInventoryLoadService#isCurrentUserInitiator(org.kuali.rice.krad.document.Document)
108      */
109     public boolean isCurrentUserInitiator(Document document) {
110         if (document != null) {
111             return GlobalVariables.getUserSession().getPerson().getPrincipalId().equalsIgnoreCase(document.getDocumentHeader().getWorkflowDocument().getInitiatorPrincipalId());
112         }
113         return false;
114     }
115 
116     /**
117      * @see org.kuali.module.cams.service.AssetBarcodeInventoryLoadService#isFileFormatValid(java.io.File)
118      */
119     public boolean isFileFormatValid(File file) {
120         LOG.debug("isFileFormatValid(File file) - start");
121         String fileName = file.getName();
122 
123         BufferedReader input = null;
124 
125         // Getting the length of each field that needs to be validated
126         Integer campusTagNumberMaxLength = dataDictionaryService.getAttributeMaxLength(Asset.class, CamsPropertyConstants.Asset.CAMPUS_TAG_NUMBER);
127         Integer inventoryScannedCodeMaxLength = new Integer(1);
128         Integer InventoryDateMaxLength = dataDictionaryService.getAttributeMaxLength(BarcodeInventoryErrorDetail.class, CamsPropertyConstants.BarcodeInventory.INVENTORY_DATE);
129         Integer campusCodeMaxLength = dataDictionaryService.getAttributeMaxLength(Asset.class, CamsPropertyConstants.Asset.CAMPUS_CODE);
130         Integer buildingCodeMaxLength = dataDictionaryService.getAttributeMaxLength(Asset.class, CamsPropertyConstants.Asset.BUILDING_CODE);
131         Integer buildingRoomNumberMaxLength = dataDictionaryService.getAttributeMaxLength(Asset.class, CamsPropertyConstants.Asset.BUILDING_ROOM_NUMBER);
132         Integer buildingSubRoomNumberMaxLength = dataDictionaryService.getAttributeMaxLength(Asset.class, CamsPropertyConstants.Asset.BUILDING_SUB_ROOM_NUMBER);
133         Integer conditionCodeMaxLength = dataDictionaryService.getAttributeMaxLength(Asset.class, CamsPropertyConstants.Asset.CONDITION_CODE);
134 
135         // Getting the label of each field from data dictionary.
136         String campusTagNumberLabel = dataDictionaryService.getAttributeLabel(Asset.class, CamsPropertyConstants.Asset.CAMPUS_TAG_NUMBER);
137         String inventoryScannedCodeLabel = dataDictionaryService.getAttributeLabel(Asset.class, CamsPropertyConstants.BarcodeInventory.UPLOAD_SCAN_INDICATOR);
138         String InventoryDateLabel = dataDictionaryService.getAttributeLabel(BarcodeInventoryErrorDetail.class, CamsPropertyConstants.BarcodeInventory.INVENTORY_DATE);
139         String campusCodeLabel = dataDictionaryService.getAttributeLabel(Asset.class, CamsPropertyConstants.Asset.CAMPUS_CODE);
140         String buildingCodeLabel = dataDictionaryService.getAttributeLabel(Asset.class, CamsPropertyConstants.Asset.BUILDING_CODE);
141         String buildingRoomNumberLabel = dataDictionaryService.getAttributeLabel(Asset.class, CamsPropertyConstants.Asset.BUILDING_ROOM_NUMBER);
142         String buildingSubRoomNumberLabel = dataDictionaryService.getAttributeLabel(Asset.class, CamsPropertyConstants.Asset.BUILDING_SUB_ROOM_NUMBER);
143         String conditionCodeLabel = dataDictionaryService.getAttributeLabel(Asset.class, CamsPropertyConstants.Asset.CONDITION_CODE);
144 
145         try {
146             int recordCount = 0;
147             String errorMsg = "";
148             String errorMessage = "";
149             boolean proceed = true;
150             String lengthError = "exceeds maximum length";
151 
152             input = new BufferedReader(new FileReader(file));
153             String line = null;
154 
155 
156             while ((line = input.readLine()) != null) {
157                 recordCount++;
158                 errorMsg = "";
159                 line = StringUtils.remove(line, "\"");
160 
161                 String[] column = org.springframework.util.StringUtils.delimitedListToStringArray(line, ",");
162 
163                 if (MAX_NUMBER_OF_COLUMNS < column.length) {
164                     // Error more columns that allowed. put it in the constants class.
165                     errorMsg += "  Barcode inventory file has record(s) with more than " + MAX_NUMBER_OF_COLUMNS + " columns\n";
166                     proceed = false;
167                 }
168                 else if (MAX_NUMBER_OF_COLUMNS > column.length) {
169                     errorMsg += "  Barcode inventory file has record(s) with less than " + MAX_NUMBER_OF_COLUMNS + " columns\n";
170                     proceed = false;
171                 }
172                 else {
173 
174                     // Validating length of each field
175                     if (column[0].length() > campusTagNumberMaxLength.intValue()) {
176                         errorMsg += ", " + campusTagNumberLabel;
177                     }
178 
179                     if (column[1].length() > inventoryScannedCodeMaxLength.intValue()) {
180                         errorMsg += ", " + inventoryScannedCodeLabel;
181                     }
182 
183                     if (column[2].length() > InventoryDateMaxLength.intValue()) {
184                         errorMsg += ", " + InventoryDateLabel;
185                     }
186 
187                     if (column[3].length() > campusCodeMaxLength.intValue()) {
188                         errorMsg += ", " + campusCodeLabel;
189                     }
190                     if (column[4].length() > buildingCodeMaxLength.intValue()) {
191                         errorMsg += ", " + buildingCodeLabel;
192                     }
193                     if (column[5].length() > buildingRoomNumberMaxLength.intValue()) {
194                         errorMsg += ", " + buildingRoomNumberLabel;
195                     }
196                     if (column[6].length() > buildingSubRoomNumberMaxLength.intValue()) {
197                         errorMsg += ", " + buildingSubRoomNumberLabel;
198                     }
199                     if (column[7].length() > conditionCodeMaxLength.intValue()) {
200                         errorMsg += ", " + conditionCodeLabel;
201                     }
202 
203                     if (!StringUtils.isBlank(errorMsg)) {
204                         errorMsg += " " + lengthError;
205                     }
206 
207                     // Validating other than the length of the fields
208                     if (!column[1].equals(CamsConstants.BarCodeInventory.BCI_SCANED_INTO_DEVICE) && !column[1].equals(CamsConstants.BarCodeInventory.BCI_MANUALLY_KEYED_CODE)) {
209                         errorMsg += ", " + inventoryScannedCodeLabel + " is invalid";
210                     }
211 
212                     // validate date 
213                     if(!validateDate(column[2])) {
214                         errorMsg += ", " + InventoryDateLabel + " is invalid";
215                     }
216 
217 
218 
219                 }
220                 if (!StringUtils.isBlank(errorMsg)) {
221                     errorMsg = "Error on record number " + recordCount + ": " + errorMsg.substring(2) + "\n";
222                     GlobalVariables.getMessageMap().putError(KFSConstants.GLOBAL_ERRORS, KFSKeyConstants.ERROR_CUSTOM, errorMsg);
223                     errorMessage += errorMsg;
224                     LOG.error(errorMsg);
225                 }
226                 if (!proceed)
227                     break;
228             }
229             if (!StringUtils.isBlank(errorMessage)) {
230                 return false;
231             }
232 
233             return true;
234         }
235         catch (FileNotFoundException e1) {
236             LOG.error("file to parse not found " + fileName, e1);
237             throw new RuntimeException("Cannot find the file requested to be parsed " + fileName + " " + e1.getMessage(), e1);
238         }
239         catch (Exception e) {
240             LOG.error("Error running file validation - File: " + fileName, e);
241             throw new IllegalArgumentException("Error running file validation - File: " + fileName);
242         }
243         finally {
244             LOG.debug("isFileFormatValid(File file) - end");
245             try {
246                 if (input != null) {
247                     input.close();
248                 }
249             }
250             catch (IOException ex) {
251                 LOG.error("isFileFormatValid() error closing file.", ex);
252             }
253         }
254 
255     }
256 
257     /**
258      * @see org.kuali.module.cams.service.AssetBarCodeInventoryLoadService#processFile(java.io.File)
259      */
260     public boolean processFile(File file, AssetBarCodeInventoryInputFileForm form) {
261         LOG.debug("processFile(File file) - start");
262         
263         // Removing *.done files that are created automatically by the framework.
264         this.removeDoneFile(file);
265 
266         BufferedReader input = null;
267         String fileName = file.getName();
268 
269         String day;
270         String month;
271         String year;
272         String hours;
273         String minutes;
274         String seconds;
275         boolean isValid = true;
276 
277         SimpleDateFormat formatter = new SimpleDateFormat(CamsConstants.DateFormats.MONTH_DAY_YEAR + " " + CamsConstants.DateFormats.MILITARY_TIME, Locale.US);
278         formatter.setLenient(false);
279         
280         BarcodeInventoryErrorDetail barcodeInventoryErrorDetail;
281         List<BarcodeInventoryErrorDetail> barcodeInventoryErrorDetails = new ArrayList<BarcodeInventoryErrorDetail>();
282 
283         List<BarcodeInventoryErrorDocument> barcodeInventoryErrorDocuments = new ArrayList<BarcodeInventoryErrorDocument>();
284         try {
285             Long ln = new Long(1);
286             input = new BufferedReader(new FileReader(file));
287             String line = null;
288 
289             while ((line = input.readLine()) != null) {
290                 line = StringUtils.remove(line, "\"");
291                 String[] lineStrings = org.springframework.util.StringUtils.delimitedListToStringArray(line, ",");
292 
293                 // Parsing date so it can be validated.
294                 lineStrings[2] = StringUtils.rightPad(lineStrings[2].trim(), 14, "0");
295 
296                 day = lineStrings[2].substring(0, 2);
297                 month = lineStrings[2].substring(2, 4);
298                 year = lineStrings[2].substring(4, 8);
299                 hours = lineStrings[2].substring(8, 10);
300                 minutes = lineStrings[2].substring(10, 12);
301                 seconds = lineStrings[2].substring(12);
302 
303                 String stringDate = month + "/" + day + "/" + year + " " + hours + ":" + minutes + ":" + seconds;
304                 Timestamp timestamp = null;
305 
306                 // If date has invalid format set its value to null
307                 try {
308                     timestamp = new Timestamp(formatter.parse(stringDate).getTime());
309                 }
310                 catch (Exception e) {
311                 }
312 
313                 // Its set to null because for some reason java parses "00000000000000" as 0002-11-30
314                 if (lineStrings[2].equals(StringUtils.repeat("0", 14))) {
315                     timestamp = null;
316                 }
317 
318                 barcodeInventoryErrorDetail = new BarcodeInventoryErrorDetail();
319                 barcodeInventoryErrorDetail.setUploadRowNumber(ln);
320                 barcodeInventoryErrorDetail.setAssetTagNumber(lineStrings[0].trim());
321                 barcodeInventoryErrorDetail.setUploadScanIndicator(lineStrings[1].equals(CamsConstants.BarCodeInventory.BCI_SCANED_INTO_DEVICE));
322                 barcodeInventoryErrorDetail.setUploadScanTimestamp(timestamp);
323                 barcodeInventoryErrorDetail.setCampusCode(lineStrings[3].trim().toUpperCase());
324                 barcodeInventoryErrorDetail.setBuildingCode(lineStrings[4].trim().toUpperCase());
325                 barcodeInventoryErrorDetail.setBuildingRoomNumber(lineStrings[5].trim().toUpperCase());
326                 barcodeInventoryErrorDetail.setBuildingSubRoomNumber(lineStrings[6].trim().toUpperCase());
327                 barcodeInventoryErrorDetail.setAssetConditionCode(lineStrings[7].trim().toUpperCase());
328                 barcodeInventoryErrorDetail.setErrorCorrectionStatusCode(CamsConstants.BarCodeInventoryError.STATUS_CODE_ERROR);
329                 barcodeInventoryErrorDetail.setCorrectorUniversalIdentifier(GlobalVariables.getUserSession().getPerson().getPrincipalId());
330 
331                 barcodeInventoryErrorDetails.add(barcodeInventoryErrorDetail);
332                 ln++;
333             }
334             processBarcodeInventory(barcodeInventoryErrorDetails, form);
335 
336             return true;
337         }
338         catch (FileNotFoundException e1) {
339             LOG.error("file to parse not found " + fileName, e1);
340             throw new RuntimeException("Cannot find the file requested to be parsed " + fileName + " " + e1.getMessage(), e1);
341         }
342         catch (Exception ex) {
343             LOG.error("Error reading file", ex);
344             throw new IllegalArgumentException("Error reading file: " + ex.getMessage(), ex);
345         }
346         finally {
347             LOG.debug("processFile(File file) - End");
348 
349             try {
350                 if (input != null) {
351                     input.close();
352                 }
353             }
354             catch (IOException ex) {
355                 LOG.error("loadFlatFile() error closing file.", ex);
356             }
357         }
358     }
359 
360     /**
361      * This method removes the *.done files. If not deleted, then the program will display the name of the file in a puldown menu
362      * with a label of ready for process.
363      * 
364      * @param file
365      */
366     protected void removeDoneFile(File file) {
367         String filePath = file.getAbsolutePath();
368         File doneFile = new File(StringUtils.substringBeforeLast(filePath, ".") + ".done");
369 
370         if (doneFile.exists()) {
371             doneFile.delete();
372         }
373     }
374 
375     /**
376      * This method invokes the rules in order to validate each records of the barcode file and invokes the method that updates the
377      * asset table with the records that passes the rules validation
378      * 
379      * @param barcodeInventoryErrorDetails
380      */
381     protected void processBarcodeInventory(List<BarcodeInventoryErrorDetail> barcodeInventoryErrorDetails, AssetBarCodeInventoryInputFileForm form) throws Exception {
382         Long lineNumber = new Long(0);
383         boolean docCreated = false;
384         int errorRecCount = 0;
385         int totalRecCount = 0;
386 
387         BarcodeInventoryErrorDocument barcodeInventoryErrorDocument = createInvalidBarcodeInventoryDocument(barcodeInventoryErrorDetails, form.getUploadDescription());
388         // apply rules for the new cash control detail
389         kualiRuleService.applyRules(new ValidateBarcodeInventoryEvent("", barcodeInventoryErrorDocument, true));
390 
391         List<BarcodeInventoryErrorDetail> tmpBarcodeInventoryErrorDetails = new ArrayList<BarcodeInventoryErrorDetail>();
392 
393         for (BarcodeInventoryErrorDetail barcodeInventoryErrorDetail : barcodeInventoryErrorDetails) {
394             totalRecCount++;
395             // if no error found, then update asset table.
396             if (!barcodeInventoryErrorDetail.getErrorCorrectionStatusCode().equals(CamsConstants.BarCodeInventoryError.STATUS_CODE_ERROR)) {
397                 this.updateAssetInformation(barcodeInventoryErrorDetail,true);
398             }
399             else {
400                 errorRecCount++;
401                 lineNumber++;
402                 // Assigning the row number to each invalid BCIE record
403                 barcodeInventoryErrorDetail.setUploadRowNumber(lineNumber);
404 
405                 // Storing in temp collection the invalid BCIE records.
406                 tmpBarcodeInventoryErrorDetails.add(barcodeInventoryErrorDetail);
407             }
408         }
409         // *********************************************************************
410         // Storing the invalid barcode inventory records.
411         // *********************************************************************
412         String documentsCreated = "";
413         if (!tmpBarcodeInventoryErrorDetails.isEmpty()) {
414             documentsCreated = this.createBarcodeInventoryErrorDocuments(tmpBarcodeInventoryErrorDetails, barcodeInventoryErrorDocument, form);
415             docCreated = true;
416         }
417 
418         if (!docCreated) {
419             form.getMessages().add(MESSAGE_NO_DOCUMENT_CREATED);
420         }
421         else {
422             // Adding the list of documents that were created in the message list
423             form.getMessages().add(DOCUMENTS_MSG + ": " + documentsCreated.substring(2));
424         }
425         form.getMessages().add(TOTAL_RECORDS_UPLOADED_MSG + ": " + StringUtils.rightPad(Integer.toString(totalRecCount), 5, " "));
426         form.getMessages().add(TOTAL_RECORDS_IN_ERROR_MSG + ": " + StringUtils.rightPad(Integer.toString(errorRecCount), 5, " "));
427     }
428 
429 
430     /**
431      * This method...
432      * 
433      * @param bcies
434      * @param barcodeInventoryErrorDocument
435      */
436     protected String createBarcodeInventoryErrorDocuments(List<BarcodeInventoryErrorDetail> bcies, BarcodeInventoryErrorDocument barcodeInventoryErrorDocument, AssetBarCodeInventoryInputFileForm form) {
437         List<BarcodeInventoryErrorDetail> barcodeInventoryErrorDetails = new ArrayList<BarcodeInventoryErrorDetail>();
438         boolean isFirstDocument = true;
439         int ln = 0;
440         int bcieCount = 0;
441         String documentsCreated = "";
442         int maxNumberRecordsPerDocument = 300;
443 
444         try {
445             if (parameterService.parameterExists(BarcodeInventoryErrorDocument.class, CamsConstants.Parameters.MAX_NUMBER_OF_RECORDS_PER_DOCUMENT)) {
446                 maxNumberRecordsPerDocument = new Integer(parameterService.getParameterValueAsString(BarcodeInventoryErrorDocument.class, CamsConstants.Parameters.MAX_NUMBER_OF_RECORDS_PER_DOCUMENT)).intValue();
447             }
448 
449             while (true) {
450                 if ((ln > maxNumberRecordsPerDocument) || (bcieCount >= bcies.size())) {
451                     // This if was added in order to not waste the document already created and not create a new one.
452                     if (!isFirstDocument) {
453                         barcodeInventoryErrorDocument = createInvalidBarcodeInventoryDocument(barcodeInventoryErrorDetails, form.getUploadDescription());
454                     }
455                     documentsCreated += ", " + barcodeInventoryErrorDocument.getDocumentNumber();
456 
457                     barcodeInventoryErrorDocument.setBarcodeInventoryErrorDetail(barcodeInventoryErrorDetails);
458                     saveInvalidBarcodeInventoryDocument(barcodeInventoryErrorDocument);
459 
460                     barcodeInventoryErrorDetails = new ArrayList<BarcodeInventoryErrorDetail>();
461 
462                     if (bcieCount >= bcies.size())
463                         break;
464 
465                     ln = 0;
466                     isFirstDocument = false;
467                 }
468 
469                 BarcodeInventoryErrorDetail barcodeInventoryErrorDetail =bcies.get(bcieCount);
470                 barcodeInventoryErrorDetail.setUploadRowNumber(Long.valueOf(ln+1));
471                 barcodeInventoryErrorDetails.add(barcodeInventoryErrorDetail);
472 
473                 ln++;
474                 bcieCount++;
475             }
476         }
477         catch (Exception e) {
478             LOG.error("Error creating BCIE documents", e);
479             throw new IllegalArgumentException("Error creating BCIE documents: " + e.getMessage(), e);
480         }
481         return documentsCreated;
482     }
483 
484 
485     /**
486      * This method updates the asset information particularly the building code, bulding room, building subrool, campus code, and
487      * condition code
488      * 
489      * @param barcodeInventoryErrorDetail
490      */
491     public void updateAssetInformation(BarcodeInventoryErrorDetail barcodeInventoryErrorDetail, boolean updateWithDateAssetWasScanned) {
492         Map<String, String> fieldValues = new HashMap<String, String>();
493         fieldValues.put(CamsPropertyConstants.Asset.CAMPUS_TAG_NUMBER, barcodeInventoryErrorDetail.getAssetTagNumber());
494         Asset asset = ((List<Asset>) businessObjectService.findMatching(Asset.class, fieldValues)).get(0);
495 
496         asset.setInventoryScannedCode((barcodeInventoryErrorDetail.isUploadScanIndicator() ? CamsConstants.BarCodeInventory.BCI_SCANED_INTO_DEVICE : CamsConstants.BarCodeInventory.BCI_MANUALLY_KEYED_CODE));
497         asset.setBuildingCode(barcodeInventoryErrorDetail.getBuildingCode());
498         asset.setBuildingRoomNumber(barcodeInventoryErrorDetail.getBuildingRoomNumber());
499         asset.setBuildingSubRoomNumber(barcodeInventoryErrorDetail.getBuildingSubRoomNumber());
500         asset.setCampusCode(barcodeInventoryErrorDetail.getCampusCode());
501         asset.setConditionCode(barcodeInventoryErrorDetail.getAssetConditionCode());        
502 
503         // set building code and room number to null if they are empty string, to avoid FK violation exception
504         if (StringUtils.isEmpty(asset.getBuildingCode())) {
505             asset.setBuildingCode(null);
506             asset.setBuilding(null);
507         }
508         if (StringUtils.isEmpty(asset.getBuildingRoomNumber())) {
509             asset.setBuildingRoomNumber(null);
510             asset.setBuildingRoom(null);
511         }        
512         
513         if (updateWithDateAssetWasScanned) {
514             asset.setLastInventoryDate(barcodeInventoryErrorDetail.getUploadScanTimestamp());
515         } else {
516             asset.setLastInventoryDate(new Timestamp(dateTimeService.getCurrentSqlDate().getTime()));
517         }
518         
519         // Purposefully deleting off-campus locations when loading locations via barcode scanning.
520         List<AssetLocation> assetLocations = asset.getAssetLocations();
521         for (AssetLocation assetLocation : assetLocations) {
522             if(CamsConstants.AssetLocationTypeCode.OFF_CAMPUS.equals(assetLocation.getAssetLocationTypeCode())) {
523                 assetLocations.remove(assetLocation);
524                 break;
525             }
526         }
527 
528         // Updating asset information
529         businessObjectService.save(asset);
530     }
531 
532     /**
533      * This method creates a transaction document with the invalid barcode inventory records
534      * 
535      * @param barcodeInventoryErrorDetails
536      * @return BarcodeInventoryErrorDocument
537      */
538     protected BarcodeInventoryErrorDocument createInvalidBarcodeInventoryDocument(List<BarcodeInventoryErrorDetail> barcodeInventoryErrorDetails, String uploadDescription) throws WorkflowException {
539         BarcodeInventoryErrorDocument document = (BarcodeInventoryErrorDocument) documentService.getNewDocument(BarcodeInventoryErrorDocument.class);
540 
541         document.getDocumentHeader().setExplanation(DOCUMENT_EXPLANATION);
542         document.getFinancialSystemDocumentHeader().setFinancialDocumentTotalAmount(KualiDecimal.ZERO);
543         document.getDocumentHeader().setDocumentDescription(uploadDescription);
544         document.setUploaderUniversalIdentifier(GlobalVariables.getUserSession().getPerson().getPrincipalId());
545         document.setBarcodeInventoryErrorDetail(barcodeInventoryErrorDetails);
546 
547         return document;
548     }
549 
550 
551     /**
552      * saves the barcode inventory document
553      * 
554      * @param document
555      */
556     protected void saveInvalidBarcodeInventoryDocument(BarcodeInventoryErrorDocument document) {
557         try {
558             // The errors are being deleted because, when the document services finds any error then, changes are not saved.
559             GlobalVariables.clear();
560 
561             // no adhoc recipient need to add when submit doc. doc will route to the doc uploader, i.e. initiator automtically.
562             List<AdHocRouteRecipient> adHocRouteRecipients = new ArrayList<AdHocRouteRecipient>();
563             documentService.routeDocument(document, "Routed Update Barcode Inventory Document", adHocRouteRecipients);
564         }
565         catch (Exception e) {
566             LOG.error("Error persisting document # " + document.getDocumentHeader().getDocumentNumber() + " " + e.getMessage(), e);
567             throw new RuntimeException("Error persisting document # " + document.getDocumentHeader().getDocumentNumber() + " " + e.getMessage(), e);
568         }
569     }
570 
571     /**
572      * This method builds a recipient for Approval.
573      * 
574      * @param userId
575      * @return
576      */
577     protected AdHocRouteRecipient buildApprovePersonRecipient(String userId) {
578         AdHocRouteRecipient adHocRouteRecipient = new AdHocRoutePerson();
579         adHocRouteRecipient.setActionRequested(KewApiConstants.ACTION_REQUEST_APPROVE_REQ);
580         adHocRouteRecipient.setId(userId);
581         return adHocRouteRecipient;
582     }
583 
584     private boolean validateDate(String date) {
585         // Parsing date so it can be validated.
586         boolean valid = true;
587         if(StringHelper.isEmpty(date)) {
588             valid = false;
589         }
590         else {
591             SimpleDateFormat formatter = new SimpleDateFormat(CamsConstants.DateFormats.MONTH_DAY_YEAR + " " + CamsConstants.DateFormats.STANDARD_TIME, Locale.US);
592             date = StringUtils.rightPad(date.trim(), 14, "0");
593             String day = date.substring(0, 2);
594             String month = date.substring(2, 4);
595             String year = date.substring(4, 8);
596             String hours = date.substring(8, 10);
597             String minutes = date.substring(10, 12);
598             String seconds = date.substring(12);
599 
600             String stringDate = month + "/" + day + "/" + year + " " + hours + ":" + minutes + ":" + seconds;
601             Timestamp timestamp = null;
602 
603             // If date has invalid format set its value to null
604             try {
605                 timestamp = new Timestamp(formatter.parse(stringDate).getTime());
606             }
607             catch (Exception e) {
608                 valid = false;
609             }
610             
611         }
612         
613         return valid;
614     }
615         public void setBusinessObjectService(BusinessObjectService businessObjectService) {
616             this.businessObjectService = businessObjectService;
617         }
618 
619         public void setDataDictionaryService(DataDictionaryService dataDictionaryService) {
620             this.dataDictionaryService = dataDictionaryService;
621         }
622 
623         public void setWorkflowDocumentService(WorkflowDocumentService workflowDocumentService) {
624             this.workflowDocumentService = workflowDocumentService;
625         }
626 
627         public void setKualiRuleService(KualiRuleService ruleService) {
628             this.kualiRuleService = ruleService;
629         }
630 
631         public void setDocumentService(DocumentService documentService) {
632             this.documentService = documentService;
633         }
634 
635         public ParameterService getParameterService() {
636             return parameterService;
637         }
638 
639         public void setParameterService(ParameterService parameterService) {
640             this.parameterService = parameterService;
641         }
642 
643         public void setDateTimeService(DateTimeService dateTimeService) {
644             this.dateTimeService = dateTimeService;
645         }
646     }