Java tutorial
/* * The Kuali Financial System, a comprehensive financial management system for higher education. * * Copyright 2005-2014 The Kuali Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.kuali.kfs.module.purap.service.impl; import java.math.BigDecimal; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.math.NumberUtils; import org.apache.log4j.Logger; import org.kuali.kfs.module.purap.PurapConstants; import org.kuali.kfs.module.purap.PurapConstants.PurchaseOrderStatuses; import org.kuali.kfs.module.purap.PurapKeyConstants; import org.kuali.kfs.module.purap.PurapParameterConstants; import org.kuali.kfs.module.purap.batch.ElectronicInvoiceStep; import org.kuali.kfs.module.purap.businessobject.ElectronicInvoice; import org.kuali.kfs.module.purap.businessobject.ElectronicInvoiceDetailRequestSummary; import org.kuali.kfs.module.purap.businessobject.ElectronicInvoiceRejectReason; import org.kuali.kfs.module.purap.businessobject.ElectronicInvoiceRejectReasonType; import org.kuali.kfs.module.purap.businessobject.PurApItem; import org.kuali.kfs.module.purap.businessobject.PurchaseOrderItem; import org.kuali.kfs.module.purap.document.PurchaseOrderDocument; import org.kuali.kfs.module.purap.service.ElectronicInvoiceMatchingService; import org.kuali.kfs.module.purap.util.ElectronicInvoiceUtils; import org.kuali.kfs.module.purap.util.PurApItemUtils; import org.kuali.kfs.sys.context.SpringContext; import org.kuali.kfs.sys.service.TaxService; import org.kuali.kfs.sys.service.impl.KfsParameterConstants; import org.kuali.kfs.vnd.businessobject.PurchaseOrderCostSource; import org.kuali.kfs.vnd.businessobject.VendorDetail; import org.kuali.kfs.vnd.document.service.VendorService; import org.kuali.rice.core.api.datetime.DateTimeService; import org.kuali.rice.core.api.util.type.KualiDecimal; import org.kuali.rice.coreservice.framework.parameter.ParameterService; import org.kuali.rice.krad.service.BusinessObjectService; import org.kuali.rice.krad.util.ObjectUtils; public class ElectronicInvoiceMatchingServiceImpl implements ElectronicInvoiceMatchingService { private Logger LOG = Logger.getLogger(ElectronicInvoiceMatchingServiceImpl.class); private Map<String, ElectronicInvoiceRejectReasonType> rejectReasonTypes; private VendorService vendorService; private TaxService taxService; private DateTimeService dateTimeService; String upperVariancePercentString; String lowerVariancePercentString; @Override public void doMatchingProcess(ElectronicInvoiceOrderHolder orderHolder) { if (LOG.isInfoEnabled()) { LOG.info("Matching process started"); } upperVariancePercentString = SpringContext.getBean(ParameterService.class).getParameterValueAsString( ElectronicInvoiceStep.class, PurapParameterConstants.ElectronicInvoiceParameters.SALES_TAX_UPPER_VARIANCE_PERCENT); lowerVariancePercentString = SpringContext.getBean(ParameterService.class).getParameterValueAsString( ElectronicInvoiceStep.class, PurapParameterConstants.ElectronicInvoiceParameters.SALES_TAX_LOWER_VARIANCE_PERCENT); ; try { if (orderHolder.isValidateHeaderInformation()) { validateHeaderInformation(orderHolder); if (orderHolder.isInvoiceRejected()) { if (LOG.isInfoEnabled()) { LOG.info("Matching process failed at header validation"); } return; } } validateInvoiceDetails(orderHolder); if (orderHolder.isInvoiceRejected()) { if (LOG.isInfoEnabled()) { LOG.info("Matching process failed at order detail validation"); } return; } } catch (NumberFormatException e) { if (LOG.isInfoEnabled()) { LOG.info("Matching process matching failed due to number format exception " + e.getMessage()); } ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.INVALID_NUMBER_FORMAT, e.getMessage(), orderHolder.getFileName()); orderHolder.addInvoiceHeaderRejectReason(rejectReason); return; } if (LOG.isInfoEnabled()) { LOG.info("Matching process ended successfully"); } } protected void validateHeaderInformation(ElectronicInvoiceOrderHolder orderHolder) { String dunsField = PurapConstants.ElectronicInvoice.RejectDocumentFields.VENDOR_DUNS_NUMBER; String applnResourceKeyName = PurapKeyConstants.ERROR_REJECT_INVALID_DUNS; if (StringUtils.isEmpty(orderHolder.getDunsNumber())) { ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.DUNS_NOT_FOUND, null, orderHolder.getFileName()); orderHolder.addInvoiceHeaderRejectReason(rejectReason, dunsField, applnResourceKeyName); return; } if (orderHolder.isRejectDocumentHolder()) { VendorDetail vendorDetail = SpringContext.getBean(VendorService.class) .getVendorByDunsNumber(orderHolder.getDunsNumber()); if (vendorDetail == null) { ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.DUNS_INVALID, null, orderHolder.getFileName()); orderHolder.addInvoiceHeaderRejectReason(rejectReason, dunsField, applnResourceKeyName); return; } } else { if (orderHolder.getVendorHeaderId() == null && orderHolder.getVendorDetailId() == null) { ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.DUNS_INVALID, null, orderHolder.getFileName()); orderHolder.addInvoiceHeaderRejectReason(rejectReason, dunsField, applnResourceKeyName); return; } } String invoiceNumberField = PurapConstants.ElectronicInvoice.RejectDocumentFields.INVOICE_FILE_NUMBER; if (!orderHolder.isInvoiceNumberAcceptIndicatorEnabled()) { if (StringUtils.isEmpty(orderHolder.getInvoiceNumber())) { ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.INVOICE_ID_EMPTY, null, orderHolder.getFileName()); orderHolder.addInvoiceHeaderRejectReason(rejectReason, invoiceNumberField, PurapKeyConstants.ERROR_REJECT_INVOICE_NUMBER_EMPTY); return; } } String invoiceDateField = PurapConstants.ElectronicInvoice.RejectDocumentFields.INVOICE_FILE_DATE; if (StringUtils.isEmpty(orderHolder.getInvoiceDateString()) || orderHolder.getInvoiceDate() == null) { ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.INVOICE_DATE_INVALID, null, orderHolder.getFileName()); orderHolder.addInvoiceHeaderRejectReason(rejectReason, invoiceDateField, PurapKeyConstants.ERROR_REJECT_INVOICE_DATE_INVALID); return; } else if (orderHolder.getInvoiceDate().after(dateTimeService.getCurrentDate())) { ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.INVOICE_DATE_GREATER, null, orderHolder.getFileName()); orderHolder.addInvoiceOrderRejectReason(rejectReason, invoiceDateField, PurapKeyConstants.ERROR_REJECT_INVOICE_DATE_GREATER); return; } if (orderHolder.isInformationOnly()) { ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.INFORMATION_ONLY, null, orderHolder.getFileName()); orderHolder.addInvoiceHeaderRejectReason(rejectReason); return; } validateSummaryAmounts(orderHolder); if (orderHolder.isInvoiceRejected()) { return; } validateItemTypes(orderHolder); if (orderHolder.isInvoiceRejected()) { return; } } protected void validateSummaryAmounts(ElectronicInvoiceOrderHolder orderHolder) { if (orderHolder.isRejectDocumentHolder()) { /** * If there are any rejects related to the summary, we're retaining it since * it's not possible to get the summary amount totals from the reject doc */ return; } ElectronicInvoiceDetailRequestSummary summary = orderHolder.getElectronicInvoice() .getInvoiceDetailRequestSummary(); boolean enableSalesTaxInd = SpringContext.getBean(ParameterService.class).getParameterValueAsBoolean( KfsParameterConstants.PURCHASING_DOCUMENT.class, PurapParameterConstants.ENABLE_SALES_TAX_IND); boolean salesTaxUsed = false; PurchaseOrderDocument poDoc = orderHolder.getPurchaseOrderDocument(); if (poDoc != null) { // we handle bad PO's in the eInvoice later, so just skip this List<PurApItem> items = PurApItemUtils.getAboveTheLineOnly(poDoc.getItems()); for (PurApItem item : items) { if (item.getItemType().isTaxableIndicator()) { salesTaxUsed = true; break; } } boolean useTaxUsed = poDoc.isUseTaxIndicator(); enableSalesTaxInd &= (salesTaxUsed || useTaxUsed); BigDecimal summaryTaxAmount = summary.getInvoiceTaxAmount(); if (!enableSalesTaxInd) { // if sales tax is disabled, total tax amount shall be zero if (summaryTaxAmount.compareTo(new BigDecimal(0)) != 0) { String extraDescription = "Summary Tax Amount:" + summaryTaxAmount; ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.TAX_SUMMARY_AMT_EXISTS, extraDescription, orderHolder.getFileName()); orderHolder.addInvoiceHeaderRejectReason(rejectReason); } } else if (orderHolder.isTaxInLine()) { validateSummaryAmount(orderHolder, summaryTaxAmount, ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_TAX, PurapConstants.ElectronicInvoice.TAX_SUMMARY_AMT_MISMATCH); } } if (orderHolder.isShippingInLine()) { validateSummaryAmount(orderHolder, summary.getInvoiceShippingAmount(), ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_SHIPPING, PurapConstants.ElectronicInvoice.SHIPPING_SUMMARY_AMT_MISMATCH); } if (orderHolder.isSpecialHandlingInLine()) { validateSummaryAmount(orderHolder, summary.getInvoiceSpecialHandlingAmount(), ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_SPECIAL_HANDLING, PurapConstants.ElectronicInvoice.SPL_HANDLING_SUMMARY_AMT_MISMATCH); } if (orderHolder.isDiscountInLine()) { validateSummaryAmount(orderHolder, summary.getInvoiceDiscountAmount(), ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_DISCOUNT, PurapConstants.ElectronicInvoice.DISCOUNT_SUMMARY_AMT_MISMATCH); } } protected void validateSummaryAmount(ElectronicInvoiceOrderHolder orderHolder, BigDecimal summaryAmount, String invoiceLineItemTypeCode, String rejectDescriptionCode) { BigDecimal lineItemTotalAmount = orderHolder.getElectronicInvoice() .getFileTotalAmountForInLineItems(invoiceLineItemTypeCode); // if (lineItemTotalAmount.compareTo(BigDecimal.ZERO) != 0) { // old way, but it's not needed if ((lineItemTotalAmount.compareTo(summaryAmount)) != 0) { String extraDescription = "Line Total Amount:" + lineItemTotalAmount + ",Summary Total Amount:" + summaryAmount; ElectronicInvoiceRejectReason rejectReason = createRejectReason(rejectDescriptionCode, extraDescription, orderHolder.getFileName()); orderHolder.addInvoiceHeaderRejectReason(rejectReason); } // } } protected void validateItemTypes(ElectronicInvoiceOrderHolder orderHolder) { validateItemMapping(orderHolder, ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_ITEM); validateItemMapping(orderHolder, ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_TAX); validateItemMapping(orderHolder, ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_SHIPPING); validateItemMapping(orderHolder, ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_SPECIAL_HANDLING); validateItemMapping(orderHolder, ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_DISCOUNT); validateItemMapping(orderHolder, ElectronicInvoice.INVOICE_AMOUNT_TYPE_CODE_EXMT); } protected void validateItemMapping(ElectronicInvoiceOrderHolder orderHolder, String kualiItemTypeCode) { if (!orderHolder.isItemTypeAvailableInItemMapping(kualiItemTypeCode)) { String extraDescription = kualiItemTypeCode; ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.ITEM_MAPPING_NOT_AVAILABLE, extraDescription, orderHolder.getFileName()); orderHolder.addInvoiceHeaderRejectReason(rejectReason); return; } } protected void validateInvoiceDetails(ElectronicInvoiceOrderHolder orderHolder) { validatePurchaseOrderMatch(orderHolder); if (orderHolder.isInvoiceRejected()) { return; } validateInvoiceItems(orderHolder); if (LOG.isInfoEnabled()) { if (!orderHolder.isInvoiceRejected()) { LOG.info("Purchase order document match done successfully"); } } } protected void validatePurchaseOrderMatch(ElectronicInvoiceOrderHolder orderHolder) { String poIDFieldName = PurapConstants.ElectronicInvoice.RejectDocumentFields.INVOICE_PO_ID; String poID = orderHolder.getInvoicePurchaseOrderID(); if (StringUtils.isEmpty(poID)) { ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.PO_ID_EMPTY, null, orderHolder.getFileName()); orderHolder.addInvoiceOrderRejectReason(rejectReason, poIDFieldName, PurapKeyConstants.ERROR_REJECT_INVOICE_POID_EMPTY); return; } String extraDesc = "Invoice Order ID:" + poID; if (!NumberUtils.isDigits(poID)) { ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.PO_ID_INVALID_FORMAT, extraDesc, orderHolder.getFileName()); orderHolder.addInvoiceOrderRejectReason(rejectReason, poIDFieldName, PurapKeyConstants.ERROR_REJECT_INVOICE_POID_INVALID); return; } PurchaseOrderDocument poDoc = orderHolder.getPurchaseOrderDocument(); if (poDoc == null) { ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.PO_NOT_EXISTS, extraDesc, orderHolder.getFileName()); orderHolder.addInvoiceOrderRejectReason(rejectReason, poIDFieldName, PurapKeyConstants.ERROR_REJECT_INVOICE__PO_NOT_EXISTS); return; } if (!poDoc.getApplicationDocumentStatus().equals(PurchaseOrderStatuses.APPDOC_OPEN)) { ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.PO_NOT_OPEN, null, orderHolder.getFileName()); orderHolder.addInvoiceOrderRejectReason(rejectReason, poIDFieldName, PurapKeyConstants.ERROR_REJECT_INVOICE_PO_CLOSED); return; } if (poDoc.getVendorHeaderGeneratedIdentifier() == null || poDoc.getVendorDetailAssignedIdentifier() == null || !(poDoc.getVendorHeaderGeneratedIdentifier().equals(orderHolder.getVendorHeaderId()) && poDoc.getVendorDetailAssignedIdentifier().equals(orderHolder.getVendorDetailId()))) { ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.PO_VENDOR_NOT_MATCHES_WITH_INVOICE_VENDOR, null, orderHolder.getFileName()); orderHolder.addInvoiceOrderRejectReason(rejectReason); return; } } protected void validateInvoiceItems(ElectronicInvoiceOrderHolder orderHolder) { Set poLineNumbers = new HashSet(); ElectronicInvoiceItemHolder[] itemHolders = orderHolder.getItems(); if (itemHolders != null) { for (int i = 0; i < itemHolders.length; i++) { validateInvoiceItem(itemHolders[i], poLineNumbers); } } } protected void validateInvoiceItem(ElectronicInvoiceItemHolder itemHolder, Set poLineNumbers) { PurchaseOrderItem poItem = itemHolder.getPurchaseOrderItem(); ElectronicInvoiceOrderHolder orderHolder = itemHolder.getInvoiceOrderHolder(); if (poItem == null) { String extraDescription = "Invoice Item Line Number:" + itemHolder.getInvoiceItemLineNumber(); ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.NO_MATCHING_PO_ITEM, extraDescription, orderHolder.getFileName()); orderHolder.addInvoiceOrderRejectReason(rejectReason, PurapConstants.ElectronicInvoice.RejectDocumentFields.INVOICE_ITEM_LINE_NUMBER, PurapKeyConstants.ERROR_REJECT_INVOICE__ITEM_NOMATCH); return; } if (poLineNumbers.contains(itemHolder.getInvoiceItemLineNumber())) { String extraDescription = "Invoice Item Line Number:" + itemHolder.getInvoiceItemLineNumber(); ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.DUPLIATE_INVOICE_LINE_ITEM, extraDescription, orderHolder.getFileName()); orderHolder.addInvoiceOrderRejectReason(rejectReason, PurapConstants.ElectronicInvoice.RejectDocumentFields.INVOICE_ITEM_LINE_NUMBER, PurapKeyConstants.ERROR_REJECT_PO_ITEM_DUPLICATE); return; } else { poLineNumbers.add(itemHolder.getInvoiceItemLineNumber()); } if (!poItem.isItemActiveIndicator()) { String extraDescription = "PO Item Line Number:" + poItem.getItemLineNumber(); ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.INACTIVE_LINE_ITEM, extraDescription, orderHolder.getFileName()); orderHolder.addInvoiceOrderRejectReason(rejectReason, PurapConstants.ElectronicInvoice.RejectDocumentFields.INVOICE_ITEM_LINE_NUMBER, PurapKeyConstants.ERROR_REJECT_PO_ITEM_INACTIVE); return; } if (!itemHolder.isCatalogNumberAcceptIndicatorEnabled()) { validateCatalogNumber(itemHolder); if (orderHolder.isInvoiceRejected()) { return; } } if (!itemHolder.isUnitOfMeasureAcceptIndicatorEnabled()) { if (!StringUtils.equals(poItem.getItemUnitOfMeasureCode(), itemHolder.getInvoiceItemUnitOfMeasureCode())) { String extraDescription = "Invoice UOM:" + itemHolder.getInvoiceItemUnitOfMeasureCode() + ", PO UOM:" + poItem.getItemUnitOfMeasureCode(); ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.UNIT_OF_MEASURE_MISMATCH, extraDescription, orderHolder.getFileName()); orderHolder.addInvoiceOrderRejectReason(rejectReason, PurapConstants.ElectronicInvoice.RejectDocumentFields.INVOICE_ITEM_UOM, PurapKeyConstants.ERROR_REJECT_UOM_MISMATCH); return; } } validateUnitPrice(itemHolder); if (orderHolder.isInvoiceRejected()) { return; } validateSalesTax(itemHolder); if (orderHolder.isInvoiceRejected()) { return; } if (poItem.getItemQuantity() != null) { validateQtyBasedItem(itemHolder); } else { validateNonQtyBasedItem(itemHolder); } } protected void validateCatalogNumber(ElectronicInvoiceItemHolder itemHolder) { PurchaseOrderItem poItem = itemHolder.getPurchaseOrderItem(); ElectronicInvoiceOrderHolder orderHolder = itemHolder.getInvoiceOrderHolder(); String invoiceCatalogNumberStripped = itemHolder.getCatalogNumberStripped(); String poCatalogNumberStripped = ElectronicInvoiceUtils.stripSplChars(poItem.getItemCatalogNumber()); /** * If Catalog number in invoice and po are not empty, create reject reason if it doesn't match */ if (StringUtils.isNotBlank(invoiceCatalogNumberStripped) && StringUtils.isNotBlank(poCatalogNumberStripped)) { if (!StringUtils.equals(poCatalogNumberStripped, invoiceCatalogNumberStripped)) { String extraDescription = "Invoice Catalog No:" + invoiceCatalogNumberStripped + ", PO Catalog No:" + poCatalogNumberStripped; ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.CATALOG_NUMBER_MISMATCH, extraDescription, orderHolder.getFileName()); orderHolder.addInvoiceOrderRejectReason(rejectReason, PurapConstants.ElectronicInvoice.RejectDocumentFields.INVOICE_ITEM_CATALOG_NUMBER, PurapKeyConstants.ERROR_REJECT_CATALOG_MISMATCH); } } else { /** * If catalog number is empty in PO/&Invoice, check whether the catalog check is required for the requisition source. * If exists in param, create reject reason. * If not exists, continue with UOM and unit price match. */ String reqSourceRequiringCatalogMatch = SpringContext.getBean(ParameterService.class) .getParameterValueAsString(ElectronicInvoiceStep.class, PurapParameterConstants.ElectronicInvoiceParameters.REQUISITION_SOURCES_REQUIRING_CATALOG_MATCHING); String requisitionSourceCodeInPO = orderHolder.getPurchaseOrderDocument().getRequisitionSourceCode(); if (StringUtils.isNotEmpty(reqSourceRequiringCatalogMatch)) { String[] requisitionSourcesFromParam = StringUtils.split(reqSourceRequiringCatalogMatch, ';'); if (ArrayUtils.contains(requisitionSourcesFromParam, requisitionSourceCodeInPO)) { String extraDescription = "Invoice Catalog No:" + invoiceCatalogNumberStripped + ", PO Catalog No:" + poItem.getItemCatalogNumber(); ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.CATALOG_NUMBER_MISMATCH, extraDescription, orderHolder.getFileName()); orderHolder.addInvoiceOrderRejectReason(rejectReason, PurapConstants.ElectronicInvoice.RejectDocumentFields.INVOICE_ITEM_CATALOG_NUMBER, PurapKeyConstants.ERROR_REJECT_CATALOG_MISMATCH); } } } } protected void validateQtyBasedItem(ElectronicInvoiceItemHolder itemHolder) { PurchaseOrderItem poItem = itemHolder.getPurchaseOrderItem(); String fileName = itemHolder.getInvoiceOrderHolder().getFileName(); ElectronicInvoiceOrderHolder orderHolder = itemHolder.getInvoiceOrderHolder(); if (KualiDecimal.ZERO.compareTo(poItem.getItemOutstandingEncumberedQuantity()) >= 0) { //we have no quantity left encumbered on the po item String extraDescription = "Invoice Item Line Number:" + itemHolder.getInvoiceItemLineNumber(); ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.OUTSTANDING_ENCUMBERED_QTY_AVAILABLE, extraDescription, orderHolder.getFileName()); orderHolder.addInvoiceOrderRejectReason(rejectReason, PurapConstants.ElectronicInvoice.RejectDocumentFields.INVOICE_ITEM_QUANTITY, PurapKeyConstants.ERROR_REJECT_POITEM_OUTSTANDING_QTY); return; } if (itemHolder.getInvoiceItemQuantity() == null) { //we have quantity entered on the PO Item but the Invoice has no quantity String extraDescription = "Invoice Item Line Number:" + itemHolder.getInvoiceItemLineNumber(); ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.INVOICE_QTY_EMPTY, extraDescription, orderHolder.getFileName()); orderHolder.addInvoiceOrderRejectReason(rejectReason, PurapConstants.ElectronicInvoice.RejectDocumentFields.INVOICE_ITEM_QUANTITY, PurapKeyConstants.ERROR_REJECT_POITEM_INVOICE_QTY_EMPTY); return; } else { if (!itemHolder.getInvoiceOrderHolder().getPurchaseOrderDocument() .isReceivingDocumentRequiredIndicator()) { if ((itemHolder.getInvoiceItemQuantity() .compareTo(poItem.getItemOutstandingEncumberedQuantity().bigDecimalValue())) > 0) { //we have more quantity on the e-invoice than left outstanding encumbered on the PO item String extraDescription = "Invoice Item Line Number:" + itemHolder.getInvoiceItemLineNumber(); ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.PO_ITEM_QTY_LESSTHAN_INVOICE_ITEM_QTY, extraDescription, orderHolder.getFileName()); orderHolder.addInvoiceOrderRejectReason(rejectReason, PurapConstants.ElectronicInvoice.RejectDocumentFields.INVOICE_ITEM_QUANTITY, PurapKeyConstants.ERROR_REJECT_POITEM_LESS_OUTSTANDING_QTY); return; } } } } protected void validateNonQtyBasedItem(ElectronicInvoiceItemHolder itemHolder) { PurchaseOrderItem poItem = itemHolder.getPurchaseOrderItem(); String fileName = itemHolder.getInvoiceOrderHolder().getFileName(); ElectronicInvoiceOrderHolder orderHolder = itemHolder.getInvoiceOrderHolder(); if ((KualiDecimal.ZERO.compareTo(poItem.getItemOutstandingEncumberedAmount())) >= 0) { //we have no dollars left encumbered on the po item String extraDescription = "Invoice Item Line Number:" + itemHolder.getInvoiceItemLineNumber(); ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.OUTSTANDING_ENCUMBERED_AMT_AVAILABLE, extraDescription, orderHolder.getFileName()); orderHolder.addInvoiceOrderRejectReason(rejectReason, PurapConstants.ElectronicInvoice.RejectDocumentFields.INVOICE_ITEM_LINE_NUMBER, PurapKeyConstants.ERROR_REJECT_POITEM_OUTSTANDING_EMCUMBERED_AMOUNT); return; } else { //we have encumbered dollars left on PO if (((itemHolder.getInvoiceItemSubTotalAmount().setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR)) .compareTo(poItem.getItemOutstandingEncumberedAmount().bigDecimalValue())) > 0) { String extraDescription = "Invoice Item Line Number:" + itemHolder.getInvoiceItemLineNumber(); ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.PO_ITEM_AMT_LESSTHAN_INVOICE_ITEM_AMT, extraDescription, orderHolder.getFileName()); orderHolder.addInvoiceOrderRejectReason(rejectReason, PurapConstants.ElectronicInvoice.RejectDocumentFields.INVOICE_ITEM_LINE_NUMBER, PurapKeyConstants.ERROR_REJECT_POITEM_LESS_OUTSTANDING_EMCUMBERED_AMOUNT); return; } } } protected void validateUnitPrice(ElectronicInvoiceItemHolder itemHolder) { PurchaseOrderCostSource costSource = itemHolder.getInvoiceOrderHolder().getPurchaseOrderDocument() .getPurchaseOrderCostSource(); PurchaseOrderItem poItem = itemHolder.getPurchaseOrderItem(); ElectronicInvoiceOrderHolder orderHolder = itemHolder.getInvoiceOrderHolder(); String extraDescription = "Invoice Item Line Number:" + itemHolder.getInvoiceItemLineNumber(); BigDecimal actualVariance = itemHolder.getInvoiceItemUnitPrice().subtract(poItem.getItemUnitPrice()); BigDecimal lowerPercentage = null; if (costSource.getItemUnitPriceLowerVariancePercent() != null) { //Checking for lower variance lowerPercentage = costSource.getItemUnitPriceLowerVariancePercent(); } else { //If the cost source itemUnitPriceLowerVariancePercent is null then //we'll use the exact match (100%). lowerPercentage = new BigDecimal(100); } BigDecimal lowerAcceptableVariance = (lowerPercentage.divide(new BigDecimal(100))) .multiply(poItem.getItemUnitPrice()).negate(); if (lowerAcceptableVariance.compareTo(actualVariance) > 0) { ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.INVOICE_AMT_LESSER_THAN_LOWER_VARIANCE, extraDescription, orderHolder.getFileName()); orderHolder.addInvoiceOrderRejectReason(rejectReason, PurapConstants.ElectronicInvoice.RejectDocumentFields.INVOICE_ITEM_UNIT_PRICE, PurapKeyConstants.ERROR_REJECT_UNITPRICE_LOWERVARIANCE); } BigDecimal upperPercentage = null; if (costSource.getItemUnitPriceUpperVariancePercent() != null) { //Checking for upper variance upperPercentage = costSource.getItemUnitPriceUpperVariancePercent(); } else { //If the cost source itemUnitPriceLowerVariancePercent is null then //we'll use the exact match (100%). upperPercentage = new BigDecimal(100); } BigDecimal upperAcceptableVariance = (upperPercentage.divide(new BigDecimal(100))) .multiply(poItem.getItemUnitPrice()); if (upperAcceptableVariance.compareTo(actualVariance) < 0) { ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.INVOICE_AMT_GREATER_THAN_UPPER_VARIANCE, extraDescription, orderHolder.getFileName()); orderHolder.addInvoiceOrderRejectReason(rejectReason, PurapConstants.ElectronicInvoice.RejectDocumentFields.INVOICE_ITEM_UNIT_PRICE, PurapKeyConstants.ERROR_REJECT_UNITPRICE_UPPERVARIANCE); } } protected void validateSalesTax(ElectronicInvoiceItemHolder itemHolder) { if (LOG.isInfoEnabled()) { LOG.info("Validating sales tax"); } ElectronicInvoiceOrderHolder orderHolder = itemHolder.getInvoiceOrderHolder(); PurchaseOrderItem poItem = itemHolder.getPurchaseOrderItem(); KualiDecimal invoiceSalesTaxAmount = new KualiDecimal(itemHolder.getTaxAmount()); boolean enableSalesTaxInd = SpringContext.getBean(ParameterService.class).getParameterValueAsBoolean( KfsParameterConstants.PURCHASING_DOCUMENT.class, PurapParameterConstants.ENABLE_SALES_TAX_IND); boolean salesTaxUsed = false; PurchaseOrderDocument poDoc = orderHolder.getPurchaseOrderDocument(); List<PurApItem> items = PurApItemUtils.getAboveTheLineOnly(poDoc.getItems()); for (PurApItem item : items) { if (item.getItemType().isTaxableIndicator()) { salesTaxUsed = true; break; } } boolean useTaxUsed = poDoc.isUseTaxIndicator(); enableSalesTaxInd &= (poItem.getItemType().isTaxableIndicator() && (salesTaxUsed || useTaxUsed)); if (LOG.isInfoEnabled()) { LOG.info("Sales Tax Enable Indicator - " + enableSalesTaxInd); LOG.info("Invoice item tax amount - " + invoiceSalesTaxAmount); } if (!enableSalesTaxInd) { // if sales tax is disabled, item tax amount shall be zero if (invoiceSalesTaxAmount.compareTo(KualiDecimal.ZERO) != 0) { String extraDescription = "Item Tax Amount:" + invoiceSalesTaxAmount; ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.TAX_SUMMARY_AMT_EXISTS, extraDescription, orderHolder.getFileName()); orderHolder.addInvoiceHeaderRejectReason(rejectReason); } return; } // For reject doc, trans date should be the einvoice processed date. java.sql.Date transTaxDate = itemHolder.getInvoiceOrderHolder().getInvoiceProcessedDate(); String deliveryPostalCode = poItem.getPurchaseOrder().getDeliveryPostalCode(); KualiDecimal extendedPrice = new KualiDecimal( getExtendedPrice(itemHolder).setScale(KualiDecimal.SCALE, KualiDecimal.ROUND_BEHAVIOR)); KualiDecimal salesTaxAmountCalculated = taxService.getTotalSalesTaxAmount(transTaxDate, deliveryPostalCode, extendedPrice); KualiDecimal actualVariance = invoiceSalesTaxAmount.subtract(salesTaxAmountCalculated); if (LOG.isInfoEnabled()) { LOG.info("Sales Tax Upper Variance param - " + upperVariancePercentString); LOG.info("Sales Tax Lower Variance param - " + lowerVariancePercentString); LOG.info("Trans date (from invoice/rejectdoc) - " + transTaxDate); LOG.info("Delivery Postal Code - " + deliveryPostalCode); LOG.info("Extended price - " + extendedPrice); LOG.info("Sales Tax amount (from sales tax service) - " + salesTaxAmountCalculated); } if (StringUtils.isNotEmpty(upperVariancePercentString)) { KualiDecimal upperVariancePercent = new KualiDecimal(upperVariancePercentString); BigDecimal upperAcceptableVariance = (upperVariancePercent.divide(new KualiDecimal(100))) .multiply(salesTaxAmountCalculated).bigDecimalValue(); if (upperAcceptableVariance.compareTo(actualVariance.bigDecimalValue()) < 0) { ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.SALES_TAX_AMT_GREATER_THAN_UPPER_VARIANCE, null, orderHolder.getFileName()); orderHolder.addInvoiceOrderRejectReason(rejectReason, PurapConstants.ElectronicInvoice.RejectDocumentFields.INVOICE_ITEM_TAX_AMT, PurapKeyConstants.ERROR_REJECT_TAXAMOUNT_UPPERVARIANCE); return; } } if (StringUtils.isNotEmpty(lowerVariancePercentString)) { KualiDecimal lowerVariancePercent = new KualiDecimal(lowerVariancePercentString); BigDecimal lowerAcceptableVariance = (lowerVariancePercent.divide(new KualiDecimal(100))) .multiply(salesTaxAmountCalculated).bigDecimalValue().negate(); if (lowerAcceptableVariance.compareTo(BigDecimal.ZERO) >= 0 && actualVariance.compareTo(KualiDecimal.ZERO) >= 0) { if (actualVariance.bigDecimalValue().compareTo(lowerAcceptableVariance) > 0) { ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.SALES_TAX_AMT_LESSER_THAN_LOWER_VARIANCE, null, orderHolder.getFileName()); orderHolder.addInvoiceOrderRejectReason(rejectReason, PurapConstants.ElectronicInvoice.RejectDocumentFields.INVOICE_ITEM_TAX_AMT, PurapKeyConstants.ERROR_REJECT_TAXAMOUNT_LOWERVARIANCE); } } else { if (actualVariance.bigDecimalValue().compareTo(lowerAcceptableVariance) < 0) { ElectronicInvoiceRejectReason rejectReason = createRejectReason( PurapConstants.ElectronicInvoice.SALES_TAX_AMT_LESSER_THAN_LOWER_VARIANCE, null, orderHolder.getFileName()); orderHolder.addInvoiceOrderRejectReason(rejectReason, PurapConstants.ElectronicInvoice.RejectDocumentFields.INVOICE_ITEM_TAX_AMT, PurapKeyConstants.ERROR_REJECT_TAXAMOUNT_LOWERVARIANCE); } } } } //Copied from PurApItemBase.calculateExtendedPrice protected BigDecimal getExtendedPrice(ElectronicInvoiceItemHolder itemHolder) { if (itemHolder.getPurchaseOrderItem().getItemType().isAmountBasedGeneralLedgerIndicator()) { // SERVICE ITEM: return unit price as extended price return itemHolder.getUnitPrice(); } else if (ObjectUtils.isNotNull(itemHolder.getQuantity())) { // qty wont be null since it's defined as a reqd field in xsd BigDecimal calcExtendedPrice = itemHolder.getUnitPrice().multiply(itemHolder.getQuantity()); // ITEM TYPE (qty driven): return (unitPrice x qty) return calcExtendedPrice; } return BigDecimal.ZERO; } @Override public ElectronicInvoiceRejectReason createRejectReason(String rejectReasonTypeCode, String extraDescription, String fileName) { ElectronicInvoiceRejectReasonType rejectReasonType = getElectronicInvoiceRejectReasonType( rejectReasonTypeCode); ElectronicInvoiceRejectReason eInvoiceRejectReason = new ElectronicInvoiceRejectReason(); if (rejectReasonType == null) { throw new NullPointerException( "Reject reason type for " + rejectReasonTypeCode + " not available in DB"); } eInvoiceRejectReason.setInvoiceFileName(fileName); eInvoiceRejectReason.setInvoiceRejectReasonTypeCode(rejectReasonTypeCode); if (StringUtils.isNotEmpty(extraDescription)) { eInvoiceRejectReason.setInvoiceRejectReasonDescription( rejectReasonType.getInvoiceRejectReasonTypeDescription() + " (" + extraDescription + ")"); } else { eInvoiceRejectReason .setInvoiceRejectReasonDescription(rejectReasonType.getInvoiceRejectReasonTypeDescription()); } return eInvoiceRejectReason; } @Override public ElectronicInvoiceRejectReasonType getElectronicInvoiceRejectReasonType(String rejectReasonTypeCode) { if (rejectReasonTypes == null) { rejectReasonTypes = getElectronicInvoiceRejectReasonTypes(); } return rejectReasonTypes.get(rejectReasonTypeCode); } protected Map<String, ElectronicInvoiceRejectReasonType> getElectronicInvoiceRejectReasonTypes() { Collection<ElectronicInvoiceRejectReasonType> collection = SpringContext .getBean(BusinessObjectService.class).findAll(ElectronicInvoiceRejectReasonType.class); Map rejectReasonTypesMap = new HashMap<String, ElectronicInvoiceRejectReasonType>(); if (collection != null && collection.size() > 0) { ElectronicInvoiceRejectReasonType[] rejectReasonTypesArr = new ElectronicInvoiceRejectReasonType[collection .size()]; collection.toArray(rejectReasonTypesArr); for (int i = 0; i < rejectReasonTypesArr.length; i++) { rejectReasonTypesMap.put(rejectReasonTypesArr[i].getInvoiceRejectReasonTypeCode(), rejectReasonTypesArr[i]); } } return rejectReasonTypesMap; } public void setVendorService(VendorService vendorService) { this.vendorService = vendorService; } public void setTaxService(TaxService taxService) { this.taxService = taxService; } public void setDateTimeService(DateTimeService dateTimeService) { this.dateTimeService = dateTimeService; } }