From 8ddff3800252d09a5b49c30d2ba98d2d2cb197e7 Mon Sep 17 00:00:00 2001 From: Daniela Balmus Date: Tue, 3 Feb 2026 10:10:47 +0000 Subject: [PATCH 01/12] KFSPTS-36246 upgrade to the 11/20/2024 version of financials --- pom.xml | 2 +- .../batch/CheckReconciliationImportStep.java | 4 +- .../kuali/kfs/cr/batch/GlTransactionStep.java | 10 +- ...tVoucherPaymentMaintenanceServiceImpl.java | 6 +- .../cg/businessobject/AgencyOrigin.java | 4 +- .../businessobject/LevelOrganization.java | 4 +- ...oneBatchFileDirectoryPathValuesFinder.java | 8 - .../kfs/module/cg/businessobject/Award.java | 35 +- .../kfs/pdp/businessobject/ACHPayee.java | 5 +- .../BatchFileLookupableHelperServiceImpl.java | 3 +- .../service/impl/BatchFileSearchService.java | 363 +++++++++--------- 11 files changed, 234 insertions(+), 210 deletions(-) delete mode 100644 src/main/java/edu/cornell/kfs/sys/businessobject/options/CreateDoneBatchFileDirectoryPathValuesFinder.java diff --git a/pom.xml b/pom.xml index 4584f29ae8..0b1eb9734a 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ true **/*Test.java.bogus - 2024-09-25 + 2024-11-20 2.2 10.17.0 diff --git a/src/main/java/com/rsmart/kuali/kfs/cr/batch/CheckReconciliationImportStep.java b/src/main/java/com/rsmart/kuali/kfs/cr/batch/CheckReconciliationImportStep.java index b16c04eef9..3860eb1863 100644 --- a/src/main/java/com/rsmart/kuali/kfs/cr/batch/CheckReconciliationImportStep.java +++ b/src/main/java/com/rsmart/kuali/kfs/cr/batch/CheckReconciliationImportStep.java @@ -54,7 +54,7 @@ import org.kuali.kfs.core.api.config.property.ConfigurationService; import org.kuali.kfs.core.api.util.type.KualiDecimal; import org.kuali.kfs.core.api.util.type.KualiInteger; -import org.kuali.kfs.krad.bo.KualiCode; +import org.kuali.kfs.core.api.mo.common.Coded; import org.kuali.kfs.krad.service.BusinessObjectService; import org.kuali.kfs.pdp.PdpConstants.PaymentStatusCodes; import org.kuali.kfs.pdp.businessobject.PaymentGroup; @@ -1170,7 +1170,7 @@ private String updateCheckStatus(CheckReconciliation cr, Collection banks, defaultStatus = statusMap.get(cr.getStatus()); oldStatus = paymentGroup.getPaymentStatusCode(); // Update PDP status and save - KualiCode code = businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, defaultStatus); + Coded code = businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, defaultStatus); if (paymentGroup.getPaymentStatus() != ((PaymentStatus) code)) { paymentGroup.setPaymentStatus((PaymentStatus) code); paymentGroup.setLastUpdatedTimestamp(new Timestamp(cr.getStatusChangeDate().getTime())); diff --git a/src/main/java/com/rsmart/kuali/kfs/cr/batch/GlTransactionStep.java b/src/main/java/com/rsmart/kuali/kfs/cr/batch/GlTransactionStep.java index acd54fe590..43ca08047e 100644 --- a/src/main/java/com/rsmart/kuali/kfs/cr/batch/GlTransactionStep.java +++ b/src/main/java/com/rsmart/kuali/kfs/cr/batch/GlTransactionStep.java @@ -33,7 +33,7 @@ import org.kuali.kfs.sys.batch.AbstractStep; import org.kuali.kfs.sys.businessobject.Bank; import org.kuali.kfs.sys.context.SpringContext; -import org.kuali.kfs.krad.bo.KualiCode; +import org.kuali.kfs.core.api.mo.common.Coded; import org.kuali.kfs.krad.service.BusinessObjectService; import org.kuali.kfs.krad.util.ObjectUtils; @@ -107,7 +107,7 @@ public boolean execute(String jobName, LocalDateTime jobRunDate) throws Interrup //glTransactionService.generateGlPendingTransactionStop(paymentGroup); - KualiCode code = businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, cr.getStatus()); + Coded code = businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, cr.getStatus()); if (paymentGroup.getPaymentStatus() != ((PaymentStatus) code)) { paymentGroup.setPaymentStatus((PaymentStatus) code); } @@ -155,7 +155,7 @@ public boolean execute(String jobName, LocalDateTime jobRunDate) throws Interrup glPendingTransactionService.generateCRCancellationGeneralLedgerPendingEntry(paymentGroup); //glTransactionService.generateGlPendingTransactionCancel(paymentGroup); - KualiCode code = businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, cr.getStatus()); + Coded code = businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, cr.getStatus()); if (paymentGroup.getPaymentStatus() != ((PaymentStatus) code)) { paymentGroup.setPaymentStatus((PaymentStatus) code); } @@ -218,7 +218,7 @@ public boolean execute(String jobName, LocalDateTime jobRunDate) throws Interrup // glTransactionService.generateGlPendingTransactionStop(paymentGroup); - KualiCode code = businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, cr.getStatus()); + Coded code = businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, cr.getStatus()); if (paymentGroup.getPaymentStatus() != ((PaymentStatus) code)) { paymentGroup.setPaymentStatus((PaymentStatus) code); } @@ -271,7 +271,7 @@ public boolean execute(String jobName, LocalDateTime jobRunDate) throws Interrup glPendingTransactionService.generateStaleGeneralLedgerPendingEntry(paymentGroup); //glPendingTransactionService.g .generateStaleGeneralLedgerPendingEntry(paymentGroup); - KualiCode code = businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, cr.getStatus()); + Coded code = businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, cr.getStatus()); if (paymentGroup.getPaymentStatus() != ((PaymentStatus) code)) { paymentGroup.setPaymentStatus((PaymentStatus) code); } diff --git a/src/main/java/edu/cornell/kfs/fp/service/impl/RecurringDisbursementVoucherPaymentMaintenanceServiceImpl.java b/src/main/java/edu/cornell/kfs/fp/service/impl/RecurringDisbursementVoucherPaymentMaintenanceServiceImpl.java index 88c7749c44..be4a45389b 100644 --- a/src/main/java/edu/cornell/kfs/fp/service/impl/RecurringDisbursementVoucherPaymentMaintenanceServiceImpl.java +++ b/src/main/java/edu/cornell/kfs/fp/service/impl/RecurringDisbursementVoucherPaymentMaintenanceServiceImpl.java @@ -21,7 +21,7 @@ import org.kuali.kfs.core.api.util.type.KualiInteger; import org.kuali.kfs.kim.impl.identity.Person; import org.kuali.kfs.kim.api.permission.PermissionService; -import org.kuali.kfs.krad.bo.KualiCode; +import org.kuali.kfs.krad.bo.CodedBase; import org.kuali.kfs.krad.service.BusinessObjectService; import org.kuali.kfs.krad.util.GlobalVariables; import org.kuali.kfs.krad.util.ObjectUtils; @@ -109,7 +109,7 @@ protected void changeStatus(PaymentGroup paymentGroup, String newPaymentStatus, } PaymentGroupHistory paymentGroupHistory = new PaymentGroupHistory(); - KualiCode cd = businessObjectService.findBySinglePrimaryKey(PaymentChangeCode.class, changeStatus); + CodedBase cd = businessObjectService.findBySinglePrimaryKey(PaymentChangeCode.class, changeStatus); paymentGroupHistory.setPaymentChange((PaymentChangeCode) cd); paymentGroupHistory.setOrigPaymentStatus(paymentGroup.getPaymentStatus()); paymentGroupHistory.setChangeUser(user); @@ -119,7 +119,7 @@ protected void changeStatus(PaymentGroup paymentGroup, String newPaymentStatus, this.businessObjectService.save(paymentGroupHistory); - KualiCode code = businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, newPaymentStatus); + CodedBase code = businessObjectService.findBySinglePrimaryKey(PaymentStatus.class, newPaymentStatus); paymentGroup.setPaymentStatus((PaymentStatus) code); this.businessObjectService.save(paymentGroup); LOG.debug("changeStatus() Status has been changed; exit method."); diff --git a/src/main/java/edu/cornell/kfs/module/cg/businessobject/AgencyOrigin.java b/src/main/java/edu/cornell/kfs/module/cg/businessobject/AgencyOrigin.java index 5a42d043cf..57c18be758 100644 --- a/src/main/java/edu/cornell/kfs/module/cg/businessobject/AgencyOrigin.java +++ b/src/main/java/edu/cornell/kfs/module/cg/businessobject/AgencyOrigin.java @@ -1,9 +1,9 @@ package edu.cornell.kfs.module.cg.businessobject; -import org.kuali.kfs.krad.bo.KualiCodeBase; +import org.kuali.kfs.krad.bo.CodedBase; import org.kuali.kfs.core.api.mo.common.active.MutableInactivatable; -public class AgencyOrigin extends KualiCodeBase implements MutableInactivatable { +public class AgencyOrigin extends CodedBase implements MutableInactivatable { private static final long serialVersionUID = 4258338743530843504L; diff --git a/src/main/java/edu/cornell/kfs/module/purap/businessobject/LevelOrganization.java b/src/main/java/edu/cornell/kfs/module/purap/businessobject/LevelOrganization.java index 39821e5942..6bc1e7eca7 100644 --- a/src/main/java/edu/cornell/kfs/module/purap/businessobject/LevelOrganization.java +++ b/src/main/java/edu/cornell/kfs/module/purap/businessobject/LevelOrganization.java @@ -1,9 +1,9 @@ package edu.cornell.kfs.module.purap.businessobject; -import org.kuali.kfs.krad.bo.KualiCodeBase; +import org.kuali.kfs.krad.bo.CodedBase; -public class LevelOrganization extends KualiCodeBase{ +public class LevelOrganization extends CodedBase{ private static final long serialVersionUID = 1L; diff --git a/src/main/java/edu/cornell/kfs/sys/businessobject/options/CreateDoneBatchFileDirectoryPathValuesFinder.java b/src/main/java/edu/cornell/kfs/sys/businessobject/options/CreateDoneBatchFileDirectoryPathValuesFinder.java deleted file mode 100644 index 36afe6d727..0000000000 --- a/src/main/java/edu/cornell/kfs/sys/businessobject/options/CreateDoneBatchFileDirectoryPathValuesFinder.java +++ /dev/null @@ -1,8 +0,0 @@ -package edu.cornell.kfs.sys.businessobject.options; - -import org.kuali.kfs.sys.businessobject.options.BatchFileDirectoryPathValuesFinder; - -public class CreateDoneBatchFileDirectoryPathValuesFinder extends BatchFileDirectoryPathValuesFinder { - - -} diff --git a/src/main/java/org/kuali/kfs/module/cg/businessobject/Award.java b/src/main/java/org/kuali/kfs/module/cg/businessobject/Award.java index c64ffe9c58..40f4d36ae2 100644 --- a/src/main/java/org/kuali/kfs/module/cg/businessobject/Award.java +++ b/src/main/java/org/kuali/kfs/module/cg/businessobject/Award.java @@ -84,7 +84,7 @@ public class Award extends PersistableBusinessObjectBase implements Billable, Mu private KualiDecimal federalFundedAmount; private Timestamp awardCreateTimestamp; private Date awardClosingDate; - private String proposalAwardTypeCode; + private String awardTypeCode; private String awardStatusCode; private String letterOfCreditFundCode; private String grantDescriptionCode; @@ -98,6 +98,7 @@ public class Award extends PersistableBusinessObjectBase implements Billable, Mu private String grantNumber; private boolean active; private String kimGroupNames; + private String researchAwardId; private List awardProjectDirectors; private AwardProjectDirector awardPrimaryProjectDirector; private List awardFundManagers; @@ -107,7 +108,7 @@ public class Award extends PersistableBusinessObjectBase implements Billable, Mu private List awardOrganizations; private Proposal proposal; - private ProposalAwardType proposalAwardType; + private AwardType awardType; private AwardStatus awardStatus; protected LetterOfCreditFund letterOfCreditFund; private GrantDescription grantDescription; @@ -239,7 +240,7 @@ public void populateFromProposal(final Proposal proposal) { setAwardProjectTitle(proposal.getProposalProjectTitle()); setAwardDirectCostAmount(proposal.getProposalDirectCostAmount()); setAwardIndirectCostAmount(proposal.getProposalIndirectCostAmount()); - setProposalAwardTypeCode(proposal.getProposalAwardTypeCode()); + setAwardTypeCode(proposal.getAwardTypeCode()); setFederalPassThroughIndicator(proposal.getProposalFederalPassThroughIndicator()); setFederalPassThroughAgencyNumber(proposal.getFederalPassThroughAgencyNumber()); setAwardPurposeCode(proposal.getProposalPurposeCode()); @@ -511,12 +512,12 @@ public void setAwardClosingDate(final Date awardClosingDate) { this.awardClosingDate = awardClosingDate; } - public String getProposalAwardTypeCode() { - return proposalAwardTypeCode; + public String getAwardTypeCode() { + return awardTypeCode; } - public void setProposalAwardTypeCode(final String proposalAwardTypeCode) { - this.proposalAwardTypeCode = proposalAwardTypeCode; + public void setAwardTypeCode(final String awardTypeCode) { + this.awardTypeCode = awardTypeCode; } public String getAwardStatusCode() { @@ -609,6 +610,14 @@ public void setActive(final boolean active) { this.active = active; } + public String getResearchAwardId() { + return researchAwardId; + } + + public void setResearchAwardId(final String researchAwardId) { + this.researchAwardId = researchAwardId; + } + public Proposal getProposal() { return proposal; } @@ -625,20 +634,20 @@ public void setProposal(final Proposal proposal) { this.proposal = proposal; } - public ProposalAwardType getProposalAwardType() { - return proposalAwardType; + public AwardType getAwardType() { + return awardType; } /** - * Sets the proposalAwardType attribute. + * Sets the awardType attribute. * * Setter is required by OJB, but should not be used to modify this attribute. This attribute is set on the initial * creation of the object and should not be changed. * - * @param proposalAwardType The proposalAwardType to set. + * @param awardType The awardType to set. */ - public void setProposalAwardType(final ProposalAwardType proposalAwardType) { - this.proposalAwardType = proposalAwardType; + public void setAwardType(final AwardType awardType) { + this.awardType = awardType; } public AwardStatus getAwardStatus() { diff --git a/src/main/java/org/kuali/kfs/pdp/businessobject/ACHPayee.java b/src/main/java/org/kuali/kfs/pdp/businessobject/ACHPayee.java index a8a2faa326..f78b3aa198 100644 --- a/src/main/java/org/kuali/kfs/pdp/businessobject/ACHPayee.java +++ b/src/main/java/org/kuali/kfs/pdp/businessobject/ACHPayee.java @@ -18,9 +18,10 @@ */ package org.kuali.kfs.pdp.businessobject; +import org.kuali.kfs.core.api.mo.common.Coded; +import org.kuali.kfs.core.api.mo.common.active.MutableInactivatable; import org.kuali.kfs.fp.businessobject.DisbursementPayee; import org.kuali.kfs.kim.impl.identity.Person; -import org.kuali.kfs.krad.bo.KualiCode; import org.kuali.kfs.krad.service.BusinessObjectService; import org.kuali.kfs.sys.context.SpringContext; import org.kuali.kfs.core.api.mo.common.active.MutableInactivatable; @@ -46,7 +47,7 @@ public void setEntityId(final String entityId) { @Override public String getPayeeTypeDescription() { - final KualiCode payeeType = SpringContext.getBean(BusinessObjectService.class).findBySinglePrimaryKey( + final Coded payeeType = SpringContext.getBean(BusinessObjectService.class).findBySinglePrimaryKey( PayeeType.class, getPayeeTypeCode()); return payeeType.getName(); } diff --git a/src/main/java/org/kuali/kfs/sys/businessobject/lookup/BatchFileLookupableHelperServiceImpl.java b/src/main/java/org/kuali/kfs/sys/businessobject/lookup/BatchFileLookupableHelperServiceImpl.java index e8da343589..2caa9a23ef 100644 --- a/src/main/java/org/kuali/kfs/sys/businessobject/lookup/BatchFileLookupableHelperServiceImpl.java +++ b/src/main/java/org/kuali/kfs/sys/businessobject/lookup/BatchFileLookupableHelperServiceImpl.java @@ -42,6 +42,7 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Path; import java.text.ParseException; import java.util.ArrayList; import java.util.Collection; @@ -82,7 +83,7 @@ public List getSearchResults(Map field } BatchFileFinder finder = new BatchFileFinder(results, filter); - List rootDirectories = BatchFileUtils.retrieveBatchFileLookupRootDirectories(); + List rootDirectories = BatchFileUtils.retrieveBatchFileLookupRootDirectories(); finder.find(rootDirectories); return results; diff --git a/src/main/java/org/kuali/kfs/sys/businessobject/service/impl/BatchFileSearchService.java b/src/main/java/org/kuali/kfs/sys/businessobject/service/impl/BatchFileSearchService.java index 1b9100b146..7fc2f1fdf4 100644 --- a/src/main/java/org/kuali/kfs/sys/businessobject/service/impl/BatchFileSearchService.java +++ b/src/main/java/org/kuali/kfs/sys/businessobject/service/impl/BatchFileSearchService.java @@ -19,13 +19,8 @@ package org.kuali.kfs.sys.businessobject.service.impl; import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.io.DirectoryWalker; +import org.apache.commons.io.FilenameUtils; import org.apache.commons.io.IOCase; -import org.apache.commons.io.filefilter.AbstractFileFilter; -import org.apache.commons.io.filefilter.FileFilterUtils; -import org.apache.commons.io.filefilter.IOFileFilter; -import org.apache.commons.io.filefilter.WildcardFileFilter; -import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; @@ -42,16 +37,18 @@ import org.kuali.kfs.sys.businessobject.service.exception.NotAllowedException; import org.kuali.kfs.sys.util.DateRangeUtil; import org.springframework.util.MultiValueMap; -import org.springframework.util.StopWatch; -import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Instant; import java.util.ArrayList; import java.util.Collection; -import java.util.Comparator; import java.util.Date; +import java.util.LinkedList; import java.util.List; -import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; import java.util.stream.Stream; public class BatchFileSearchService extends SearchService { @@ -69,9 +66,7 @@ public Pair, Integer> getSearchResults( final String sortField, final boolean sortAscending ) { - final StopWatch stopWatch = new StopWatch(); - stopWatch.start(); - LOG.info( + LOG.trace( "getSearchResults(...) - Enter : businessObjectClass={}, fieldValues={}, skip={}, limit={}, " + "sortField={}, sortAscending={}", businessObjectClass::getSimpleName, @@ -97,11 +92,16 @@ public Pair, Integer> getSearchResults( ); final Pair, Integer> pair = Pair.of(sortedAndSliced, allFiles.size()); - stopWatch.stop(); - LOG.info( - "getSearchResults(...) - Exit : pair={}; elapsedMillis={}", - () -> pair, - stopWatch::getTotalTimeMillis + LOG.trace( + "getSearchResults(...) - Exit : businessObjectClass={}; fieldValues={}; skip={}; limit={}; " + + "sortField={}; sortAscending={}; pair={}", + businessObjectClass::getSimpleName, + () -> fieldValues, + () -> skip, + () -> limit, + () -> sortField, + () -> sortAscending, + () -> pair ); return pair; } @@ -130,62 +130,169 @@ public Object find(final Class businessObjectClass } } - private List getFiles(final MultiValueMap fieldValues) { - final StopWatch stopWatch = new StopWatch(); - stopWatch.start(); - LOG.info("getFiles(...) - Enter"); + private static List getFiles(final MultiValueMap fieldValues) { + LOG.trace("getFiles(...) - Enter : fieldValues={}", fieldValues); + + final String fileNameWildcard = fieldValues.getFirst("fileName"); + + final String lastModifiedDate = fieldValues.getFirst("lastModifiedDate"); + final DateRangeUtil lastModifiedDateRangeUtil = getLastModifierDateRangeUtil(lastModifiedDate); + Instant startInstant = null; + Instant endInstant = null; + if (lastModifiedDateRangeUtil != null) { + final Date lowerDate = lastModifiedDateRangeUtil.getLowerDate(); + if (lowerDate != null) { + startInstant = lowerDate.toInstant(); + } + final Date upperDate = lastModifiedDateRangeUtil.getUpperDate(); + if (upperDate != null) { + endInstant = upperDate.toInstant(); + } + } + + final List userSelectedPaths = + Optional.ofNullable(fieldValues.get("path")) + .map(paths -> paths.stream().map(Path::of).collect(Collectors.toList())) + .orElseGet(List::of); + final List directoriesToSearch = getDirectoriesToSearch(userSelectedPaths); final List results = new ArrayList<>(); + for (final Path directory : directoriesToSearch) { + results.addAll(searchDirectory(directory, fileNameWildcard, startInstant, endInstant)); + } - IOFileFilter filter = FileFilterUtils.fileFileFilter(); + LOG.trace( + "getFiles(...) - Exit : fieldValues={}; results.size={}", + () -> fieldValues, + results::size + ); + return results; + } - final String fileNamePattern = fieldValues.getFirst("fileName"); - final IOFileFilter fileNameBasedFilter = getFileNameBasedFilter(fileNamePattern); - if (fileNameBasedFilter != null) { - filter = FileFilterUtils.and(filter, fileNameBasedFilter); + private static Collection searchDirectory( + final Path directory, + final String fileNameWildcard, + final Instant startInstant, + final Instant endInstant + ) { + LOG.trace( + "searchDirectory(...) - Enter : directory={}; fileNameWildcard={}; startInstant={}; endInstant={}", + directory::toString, + () -> fileNameWildcard, + () -> startInstant, + () -> endInstant + ); + + try (Stream paths = Files.walk(directory)) { + final List batchFiles = + paths.filter(Files::isRegularFile) + .filter(file -> matchesCriteria(file, fileNameWildcard, startInstant, endInstant)) + .map(Path::toFile) + .map(BatchFile::new) + .collect(Collectors.toList()); + LOG.trace("searchDirectory(...) - Exit : directory={}; fileNameWildcard={}; startInstant={}; " + + "endInstant={}; batchFiles.size={}", + directory::toString, + () -> fileNameWildcard, + () -> startInstant, + () -> endInstant, + batchFiles::size + ); + return batchFiles; + } catch (final IOException e) { + LOG.atError() + .withThrowable(e) + .log("searchDirectory(...) - Error accessing directory : directory={}", directory); + return List.of(); } + } - final String lastModifiedDate = fieldValues.getFirst("lastModifiedDate"); - final IOFileFilter lastModifiedDateBasedFilter = getLastModifiedDateBasedFilter(lastModifiedDate); - if (lastModifiedDateBasedFilter != null) { - filter = FileFilterUtils.and(filter, lastModifiedDateBasedFilter); + private static boolean matchesCriteria( + final Path file, + final String fileNameWildcard, + final Instant startInstant, + final Instant endInstant + ) { + return nameMatchesWildcard(file, fileNameWildcard) + && lastModificationInDesiredRange(file, startInstant, endInstant); + } + + private static boolean nameMatchesWildcard(final Path file, final String fileNameWildcard) { + LOG.trace( + "nameMatchesWildcard(...) - Enter : file={}; fileNameWildcard={}", + file::toString, + () -> fileNameWildcard + ); + + if (StringUtils.isBlank(fileNameWildcard)) { + return true; } - final List pathPatterns = fieldValues.get("path"); - final List directories = getDirectoriesToSearch(pathPatterns); - final BatchFileSearchService.BatchFileFinder finder = - new BatchFileSearchService.BatchFileFinder(results, filter); - finder.find(directories); - - stopWatch.stop(); - LOG.info( - "getFiles(...) - Exit : results.size={}; elapsedMillis={}", - results::size, - stopWatch::getTotalTimeMillis + final boolean match = + FilenameUtils.wildcardMatch(file.getFileName().toString(), fileNameWildcard, IOCase.INSENSITIVE); + LOG.trace( + "nameMatchesWildcard(...) - Exit : file={}; fileNameWildcard={}; match={}", + file::toString, + () -> fileNameWildcard, + () -> match ); - return results; + return match; } - private IOFileFilter getFileNameBasedFilter(final String fileNamePattern) { - if (StringUtils.isNotBlank(fileNamePattern)) { - return new WildcardFileFilter(fileNamePattern, IOCase.INSENSITIVE); + private static boolean lastModificationInDesiredRange( + final Path file, + final Instant startInstant, + final Instant endInstant + ) { + LOG.trace( + "lastModificationInDesiredRange(...) - Enter : file={}; startInstant={}; endInstant={}", + file::toString, + () -> startInstant, + () -> endInstant + ); + + if (startInstant == null && endInstant == null) { + return true; + } + + try { + final Instant lastModifiedInstant = Files.getLastModifiedTime(file).toInstant(); + + final boolean inRange = + (startInstant == null || !startInstant.isAfter(lastModifiedInstant)) + && (endInstant == null || !endInstant.isBefore(lastModifiedInstant)); + LOG.trace( + "lastModificationInDesiredRange(...) - Exit : file={}; startInstant={}; endInstant={}; inRange={}", + file::toString, + () -> startInstant, + () -> endInstant, + () -> inRange + ); + return inRange; + } catch (final IOException e) { + LOG.atError().withThrowable(e).log( + "lastModificationInDesiredRange(...) - Error reading file attributes: file={}", + file + ); + return false; } - return null; } - private IOFileFilter getLastModifiedDateBasedFilter(final String lastModifiedDatePattern) { + private static DateRangeUtil getLastModifierDateRangeUtil(final String lastModifiedDatePattern) { + LOG.trace("getLastModifierDateRangeUtil(...) - Enter : lastModifiedDatePattern={}", lastModifiedDatePattern); + if (StringUtils.isBlank(lastModifiedDatePattern)) { return null; } final DateRangeUtil dateRange = new DateRangeUtil(); dateRange.setDateStringWithLongValues(lastModifiedDatePattern); - if (!dateRange.isEmpty()) { - return new BatchFileSearchService.LastModifiedDateFileFilter(dateRange.getLowerDate(), - dateRange.getUpperDate()); - } else { + + if (dateRange.isEmpty()) { throw new RuntimeException("Unable to perform search using last modified date " + lastModifiedDatePattern); } + LOG.trace("getLastModifierDateRangeUtil(...) - Exit : lastModifiedDatePattern={}", lastModifiedDatePattern); + return dateRange; } /* @@ -193,62 +300,51 @@ private IOFileFilter getLastModifiedDateBasedFilter(final String lastModifiedDat * that it can be overridden in unit test * */ - protected List getDirectoriesToSearch(final List selectedPaths) { - final StopWatch stopWatch = new StopWatch(); - stopWatch.start(); - LOG.info("getDirectoriesToSearch(...) - Enter : selectedPaths={}", selectedPaths); - - final List searchPaths = getPathsToSearch(selectedPaths); - - List directories = new ArrayList<>(); - if (selectedPaths != null) { - for (final String searchPath : searchPaths) { - final File directory = new File(BatchFileUtils.resolvePathToAbsolutePath(searchPath)); - if (directory.exists()) { - directories.add(directory); + protected static List getDirectoriesToSearch(final List userSelectedPaths) { + LOG.trace("getDirectoriesToSearch(...) - Enter : userSelectedPaths={}", userSelectedPaths); + + final List uniqueSelectedDirectories = getUniqueSelectedDirectories(userSelectedPaths); + + List directoriesToSearch = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(userSelectedPaths)) { + for (final Path uniqueSelectedDirectory : uniqueSelectedDirectories) { + final Path directory = BatchFileUtils.resolvePathToAbsolutePath(uniqueSelectedDirectory); + if (Files.exists(directory)) { + directoriesToSearch.add(directory); } } } else { - directories = BatchFileUtils.retrieveBatchFileLookupRootDirectories(); + directoriesToSearch = BatchFileUtils.retrieveBatchFileLookupRootDirectories(); } - stopWatch.stop(); - LOG.info( - "getDirectoriesToSearch(...) - Exit : directories.size={}; elapsedMillis={}", - directories::size, - stopWatch::getTotalTimeMillis + LOG.trace( + "getDirectoriesToSearch(...) - Exit : userSelectedPaths={}; directoriesToSearch.size={}", + () -> userSelectedPaths, + directoriesToSearch::size ); - return directories; + return directoriesToSearch; } - private List getPathsToSearch(final List selectedPaths) { - final StopWatch stopWatch = new StopWatch(); - stopWatch.start(); - LOG.info("getPathsToSearch(...) - Enter : selectedPaths={}", selectedPaths); + // Ignore redundant child paths + private static List getUniqueSelectedDirectories(final List userSelectedDirectories) { + LOG.trace("getUniqueSelectedDirectories(...) - Enter : userSelectedDirectories={}", userSelectedDirectories); - if (CollectionUtils.isEmpty(selectedPaths)) { - LOG.info("getPathsToSearch(...) - Exit : selectedPaths is empty"); - return selectedPaths; + final List uniqueSelectedDirectories = new LinkedList<>(userSelectedDirectories); + + for (final Path path1 : userSelectedDirectories) { + for (final Path path2 : userSelectedDirectories) { + if (!path1.equals(path2) && path2.startsWith(path1)) { + uniqueSelectedDirectories.remove(path2); + } + } } - // Ignore redundant child paths - final List searchPaths = new ArrayList<>(); - selectedPaths.stream() - .sorted(Comparator.comparingInt(String::length)) - .forEach(selectedPath -> { - final String[] searchPathsStringArray = searchPaths.toArray(ArrayUtils.EMPTY_STRING_ARRAY); - if (!StringUtils.startsWithAny(selectedPath, searchPathsStringArray)) { - searchPaths.add(selectedPath); - } - }); - - stopWatch.stop(); - LOG.info( - "getPathsToSearch(...) - Exit : searchPaths.size={}; elapsedMillis={}", - searchPaths::size, - stopWatch::getTotalTimeMillis + LOG.trace( + "getUniqueSelectedDirectories(...) - Exit : userSelectedDirectories={}; uniqueSelectedDirectories={}", + userSelectedDirectories, + uniqueSelectedDirectories ); - return searchPaths; + return uniqueSelectedDirectories; } public void setBusinessObjectDictionaryService( @@ -256,79 +352,4 @@ public void setBusinessObjectDictionaryService( this.businessObjectDictionaryService = businessObjectDictionaryService; } - static final class BatchFileFinder extends DirectoryWalker { - private static final Logger LOG = LogManager.getLogger(); - - private final List results; - - BatchFileFinder(final List results, final IOFileFilter fileFilter) { - super(null, fileFilter, -1); - this.results = results; - } - - void find(final Collection rootDirectories) { - final StopWatch stopWatch = new StopWatch(); - stopWatch.start(); - LOG.info("find(...) - Enter : rootDirectories={}", rootDirectories); - - try { - for (final File rootDirectory : rootDirectories) { - walk(rootDirectory, null); - } - } catch (final IOException e) { - throw new RuntimeException("Error performing lookup", e); - } - - stopWatch.stop(); - LOG.info( - "find(...) - Exit : results.size={}; elapsedMillis={}", - results::size, - stopWatch::getTotalTimeMillis - ); - } - - /** - * @see org.apache.commons.io.DirectoryWalker#handleFile(java.io.File, int, java.util.Collection) - */ - @Override - protected void handleFile(final File file, final int depth, final Collection results) throws IOException { - final StopWatch stopWatch = new StopWatch(); - stopWatch.start(); - LOG.info( - "handleFile(...) - Enter : file={}; results.size={}", - () -> file, - () -> Objects.requireNonNullElseGet(results, List::of).size() - ); - - super.handleFile(file, depth, results); - final BatchFile batchFile = new BatchFile(file); - this.results.add(batchFile); - - stopWatch.stop(); - LOG.info( - "handleFile(...) - Exit : results.size={}; elapsedMillis={}", - () -> Objects.requireNonNullElseGet(results, List::of).size(), - stopWatch::getTotalTimeMillis - ); - } - } - - protected class LastModifiedDateFileFilter extends AbstractFileFilter { - private final Date fromDate; - private final Date toDate; - - LastModifiedDateFileFilter(final Date fromDate, final Date toDate) { - this.fromDate = fromDate; - this.toDate = toDate; - } - - @Override - public boolean accept(final File file) { - final Date lastModifiedDate = new Date(file.lastModified()); - if (fromDate != null && fromDate.after(lastModifiedDate)) { - return false; - } - return toDate == null || !toDate.before(lastModifiedDate); - } - } } From 6b2d60a9aebb64c7551fd350a215e6706b598b86 Mon Sep 17 00:00:00 2001 From: Daniela Balmus Date: Wed, 4 Feb 2026 14:07:11 +0000 Subject: [PATCH 02/12] KFSPTS-36246 add more changes --- .../lookup/CuBatchFileLookupableHelperServiceImpl.java | 5 +++-- .../lookup/BatchFileLookupableHelperServiceImpl.java | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/edu/cornell/kfs/sys/businessobject/lookup/CuBatchFileLookupableHelperServiceImpl.java b/src/main/java/edu/cornell/kfs/sys/businessobject/lookup/CuBatchFileLookupableHelperServiceImpl.java index 76aa2e1181..de0b1d34c1 100644 --- a/src/main/java/edu/cornell/kfs/sys/businessobject/lookup/CuBatchFileLookupableHelperServiceImpl.java +++ b/src/main/java/edu/cornell/kfs/sys/businessobject/lookup/CuBatchFileLookupableHelperServiceImpl.java @@ -1,6 +1,7 @@ package edu.cornell.kfs.sys.businessobject.lookup; import java.io.File; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -35,7 +36,7 @@ public List getSearchResults(Map field BatchFileFinder finder = new BatchFileFinder(results, filter); List selectedDirectories = getSelectedDirectories(getSelectedPaths()); if (selectedDirectories.isEmpty()) { - List rootDirectories = retrieveRootDirectories(); + List rootDirectories = retrieveRootDirectories(); finder.find(rootDirectories); } else { finder.find(selectedDirectories); @@ -44,7 +45,7 @@ public List getSearchResults(Map field return results; } - protected List retrieveRootDirectories() { + protected List retrieveRootDirectories() { return BatchFileUtils.retrieveBatchFileLookupRootDirectories(); } diff --git a/src/main/java/org/kuali/kfs/sys/businessobject/lookup/BatchFileLookupableHelperServiceImpl.java b/src/main/java/org/kuali/kfs/sys/businessobject/lookup/BatchFileLookupableHelperServiceImpl.java index 2caa9a23ef..544536a0f9 100644 --- a/src/main/java/org/kuali/kfs/sys/businessobject/lookup/BatchFileLookupableHelperServiceImpl.java +++ b/src/main/java/org/kuali/kfs/sys/businessobject/lookup/BatchFileLookupableHelperServiceImpl.java @@ -83,7 +83,7 @@ public List getSearchResults(Map field } BatchFileFinder finder = new BatchFileFinder(results, filter); - List rootDirectories = BatchFileUtils.retrieveBatchFileLookupRootDirectories(); + List rootDirectories = BatchFileUtils.retrieveBatchFileLookupRootDirectories(); finder.find(rootDirectories); return results; @@ -316,4 +316,5 @@ public void validateSearchParameters(Map fieldValues) { public void setBatchFileAdminAuthorizationService(BatchFileAdminAuthorizationService batchFileAdminAuthorizationService) { this.batchFileAdminAuthorizationService = batchFileAdminAuthorizationService; } + } From 7584722404a39765d644629277e67f91f11890c1 Mon Sep 17 00:00:00 2001 From: Daniela Balmus Date: Thu, 5 Feb 2026 15:55:32 +0000 Subject: [PATCH 03/12] KFSPTS-36246 add more changes --- ...uBatchFileLookupableHelperServiceImpl.java | 3 +- .../web/struts/PurchasingActionBase.java | 138 ++++++------ .../BatchFileLookupableHelperServiceImpl.java | 6 +- .../web/struts/KualiBatchFileAdminAction.java | 207 ++++++++++++++++++ 4 files changed, 285 insertions(+), 69 deletions(-) create mode 100644 src/main/java/org/kuali/kfs/sys/web/struts/KualiBatchFileAdminAction.java diff --git a/src/main/java/edu/cornell/kfs/sys/businessobject/lookup/CuBatchFileLookupableHelperServiceImpl.java b/src/main/java/edu/cornell/kfs/sys/businessobject/lookup/CuBatchFileLookupableHelperServiceImpl.java index de0b1d34c1..d859016340 100644 --- a/src/main/java/edu/cornell/kfs/sys/businessobject/lookup/CuBatchFileLookupableHelperServiceImpl.java +++ b/src/main/java/edu/cornell/kfs/sys/businessobject/lookup/CuBatchFileLookupableHelperServiceImpl.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import org.apache.commons.io.filefilter.FileFilterUtils; import org.apache.commons.io.filefilter.IOFileFilter; @@ -36,7 +37,7 @@ public List getSearchResults(Map field BatchFileFinder finder = new BatchFileFinder(results, filter); List selectedDirectories = getSelectedDirectories(getSelectedPaths()); if (selectedDirectories.isEmpty()) { - List rootDirectories = retrieveRootDirectories(); + List rootDirectories = retrieveRootDirectories().stream().map(Path::toFile).collect(Collectors.toList()); finder.find(rootDirectories); } else { finder.find(selectedDirectories); diff --git a/src/main/java/org/kuali/kfs/module/purap/document/web/struts/PurchasingActionBase.java b/src/main/java/org/kuali/kfs/module/purap/document/web/struts/PurchasingActionBase.java index edcded330d..0ed074fae3 100644 --- a/src/main/java/org/kuali/kfs/module/purap/document/web/struts/PurchasingActionBase.java +++ b/src/main/java/org/kuali/kfs/module/purap/document/web/struts/PurchasingActionBase.java @@ -174,7 +174,8 @@ public ActionForward refresh( GlobalVariables.getMessageMap().removeFromErrorPath(PurapConstants.DELIVERY_TAB_ERRORS); } - Integer requestorNameMaxLength = SpringContext.getBean(DataDictionaryService.class).getAttributeMaxLength(document.getClass(), PurapPropertyConstants.REQUESTOR_PERSON_NAME); + Integer requestorNameMaxLength = SpringContext.getBean(DataDictionaryService.class) + .getAttributeMaxLength(document.getClass(), PurapPropertyConstants.REQUESTOR_PERSON_NAME); // KFSPTS-518/KFSUPGRADE-351 if (requestorNameMaxLength == null && document instanceof PurchaseOrderAmendmentDocument) { requestorNameMaxLength = SpringContext.getBean(DataDictionaryService.class).getAttributeMaxLength(PurchaseOrderDocument.class, PurapPropertyConstants.REQUESTOR_PERSON_NAME); @@ -452,11 +453,10 @@ private Building findBuilding(final String buildingCode, final String campusCode * @param request A HttpServletRequest * @param response A HttpServletResponse * @return An ActionForward - * @throws Exception */ public ActionForward useOtherDeliveryBuilding( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PurchasingFormBase baseForm = (PurchasingFormBase) form; final PurchasingDocument document = (PurchasingDocument) baseForm.getDocument(); @@ -491,7 +491,7 @@ public ActionForward useOffCampusAssetLocationBuildingByDocument( public ActionForward useOffCampusAssetLocationBuildingByItem( final ActionMapping mapping, final ActionForm form, - final HttpServletRequest request, final HttpServletResponse response) throws Exception { + final HttpServletRequest request, final HttpServletResponse response) { final PurchasingFormBase baseForm = (PurchasingFormBase) form; final PurchasingDocument document = (PurchasingDocument) baseForm.getDocument(); @@ -536,11 +536,10 @@ protected void useOffCampusAssetLocationBuilding(final CapitalAssetLocation loca * @param request The HttpServletRequest * @param response The HttpServletResponse * @return An ActionForward - * @throws Exception */ public ActionForward addItem( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PurchasingFormBase purchasingForm = (PurchasingFormBase) form; PurApItem item = purchasingForm.getNewPurchasingItemLine(); final PurchasingDocument purDocument = (PurchasingDocument) purchasingForm.getDocument(); @@ -565,7 +564,7 @@ public ActionForward addItem( // Used in puritems.tag public ActionForward importItems( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { LOG.info("Importing item lines"); final PurchasingFormBase purchasingForm = (PurchasingFormBase) form; @@ -637,11 +636,10 @@ protected void updateBOReferenceforNewItems(final List importedItems, * @param request The HttpServletRequest * @param response The HttpServletResponse * @return An ActionForward - * @throws Exception */ public ActionForward deleteItem( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PurchasingAccountsPayableFormBase purchasingForm = (PurchasingAccountsPayableFormBase) form; final PurchasingDocument purDocument = (PurchasingDocument) purchasingForm.getDocument(); purDocument.deleteItem(getSelectedLine(request)); @@ -665,11 +663,10 @@ public ActionForward deleteItem( * @param request The HttpServletRequest * @param response The HttpServletResponse * @return An ActionForward - * @throws Exception */ public ActionForward upItem( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PurchasingAccountsPayableFormBase purchasingForm = (PurchasingAccountsPayableFormBase) form; final PurchasingDocument purDocument = (PurchasingDocument) purchasingForm.getDocument(); final int line = getSelectedLine(request); @@ -686,11 +683,10 @@ public ActionForward upItem( * @param request The HttpServletRequest * @param response The HttpServletResponse * @return An ActionForward - * @throws Exception */ public ActionForward downItem( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PurchasingAccountsPayableFormBase purchasingForm = (PurchasingAccountsPayableFormBase) form; final PurchasingDocument purDocument = (PurchasingDocument) purchasingForm.getDocument(); final int line = getSelectedLine(request); @@ -706,12 +702,11 @@ public ActionForward downItem( * @param request The HttpServletRequest * @param response The HttpServletResponse * @return An ActionForward - * @throws Exception */ @SuppressWarnings("unchecked") public ActionForward setupAccountDistribution( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PurchasingFormBase purchasingForm = (PurchasingFormBase) form; final PurchasingDocumentBase document = (PurchasingDocumentBase) purchasingForm.getDocument(); @@ -733,11 +728,10 @@ public ActionForward setupAccountDistribution( * @param request The HttpServletRequest * @param response The HttpServletResponse * @return An ActionForward - * @throws Exception */ public ActionForward removeAccounts( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PurchasingAccountsPayableFormBase purchasingForm = (PurchasingAccountsPayableFormBase) form; final Object question = request.getParameter(PurapConstants.QUESTION_INDEX); @@ -768,11 +762,10 @@ public ActionForward removeAccounts( * @param request The HttpServletRequest * @param response The HttpServletResponse * @return An ActionForward - * @throws Exception */ public ActionForward clearItemsCommodityCodes( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PurchasingAccountsPayableFormBase purchasingForm = (PurchasingAccountsPayableFormBase) form; final Object question = request.getParameter(PurapConstants.QUESTION_INDEX); @@ -833,11 +826,10 @@ protected boolean validateDistributeAccounts( * @param request The HttpServletRequest * @param response The HttpServletResponse * @return An ActionForward - * @throws Exception */ public ActionForward doDistribution( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PurchasingFormBase purchasingForm = (PurchasingFormBase) form; boolean needToDistributeCommodityCode = false; @@ -982,11 +974,10 @@ public ActionForward doDistribution( * @param request The HttpServletRequest * @param response The HttpServletResponse * @return An ActionForward - * @throws Exception */ public ActionForward cancelAccountDistribution( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PurchasingFormBase purchasingForm = (PurchasingFormBase) form; purchasingForm.setHideDistributeAccounts(true); return mapping.findForward(KFSConstants.MAPPING_BASIC); @@ -1017,7 +1008,7 @@ public boolean processCustomInsertAccountingLine( @Override public ActionForward deleteSourceLine( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PurchasingFormBase purchasingForm = (PurchasingFormBase) form; final String[] indexes = getSelectedLineForAccounts(request); @@ -1059,10 +1050,10 @@ protected SourceAccountingLine customAccountRetrieval( * @param request * @param response * @return - * @throws Exception */ - public ActionForward selectSystemType(final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, final HttpServletResponse response) throws Exception { - + public ActionForward selectSystemType( + final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, + final HttpServletResponse response) { final PurchasingAccountsPayableFormBase purchasingForm = (PurchasingAccountsPayableFormBase) form; final PurchasingDocumentBase document = (PurchasingDocumentBase) purchasingForm.getDocument(); @@ -1073,15 +1064,15 @@ public ActionForward selectSystemType(final ActionMapping mapping, final ActionF systemTypeCode = StringUtils.substringBetween(systemTypeCode, "selectSystemType.", "."); if (question == null) { - final String questionText = SpringContext.getBean(ConfigurationService.class).getPropertyValueAsString(PurapConstants.CapitalAssetTabStrings.QUESTION_SYSTEM_SWITCHING); - - return this.performQuestionWithoutInput(mapping, form, request, response, PurapConstants.CapitalAssetTabStrings.SYSTEM_SWITCHING_QUESTION, questionText, KFSConstants.CONFIRMATION_QUESTION, KFSConstants.ROUTE_METHOD, "0"); - } - else if (ConfirmationQuestion.YES.equals(buttonClicked)) { + final String questionText = SpringContext.getBean(ConfigurationService.class) + .getPropertyValueAsString(PurapConstants.CapitalAssetTabStrings.QUESTION_SYSTEM_SWITCHING); + return performQuestionWithoutInput(mapping, form, request, response, + PurapConstants.CapitalAssetTabStrings.SYSTEM_SWITCHING_QUESTION, questionText, + KFSConstants.CONFIRMATION_QUESTION, KFSConstants.ROUTE_METHOD, "0"); + } else if (ConfirmationQuestion.YES.equals(buttonClicked)) { // document.setCapitalAssetSystemTypeCode(systemTypeCode); document.refreshReferenceObject(PurapPropertyConstants.CAPITAL_ASSET_SYSTEM_TYPE); - KNSGlobalVariables.getMessageList().add(PurapKeyConstants.PUR_CAPITAL_ASSET_SYSTEM_TYPE_SWITCHED); } @@ -1090,7 +1081,7 @@ else if (ConfirmationQuestion.YES.equals(buttonClicked)) { public ActionForward addItemCapitalAssetByDocument( final ActionMapping mapping, final ActionForm form, - final HttpServletRequest request, final HttpServletResponse response) throws Exception { + final HttpServletRequest request, final HttpServletResponse response) { final PurchasingFormBase purchasingForm = (PurchasingFormBase) form; final PurchasingDocument purDocument = (PurchasingDocument) purchasingForm.getDocument(); ItemCapitalAsset asset = purDocument.getPurchasingCapitalAssetItems().get(0).getNewPurchasingItemCapitalAssetLine(); @@ -1114,23 +1105,26 @@ public ActionForward addItemCapitalAssetByDocument( return mapping.findForward(KFSConstants.MAPPING_BASIC); } - public ActionForward addItemCapitalAssetByItem(final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, final HttpServletResponse response) throws Exception { + public ActionForward addItemCapitalAssetByItem( + final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, + final HttpServletResponse response) { final PurchasingFormBase purchasingForm = (PurchasingFormBase) form; final PurchasingDocument purDocument = (PurchasingDocument) purchasingForm.getDocument(); - // get specific asset item - final PurchasingCapitalAssetItem assetItem = purDocument.getPurchasingCapitalAssetItems().get(getSelectedLine(request)); + final PurchasingCapitalAssetItem assetItem = purDocument.getPurchasingCapitalAssetItems() + .get(getSelectedLine(request)); ItemCapitalAsset asset = assetItem.getNewPurchasingItemCapitalAssetLine(); - final boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new AttributedAddPurchasingItemCapitalAssetEvent("", purDocument, asset)); - + final boolean rulePassed = SpringContext.getBean(KualiRuleService.class) + .applyRules(new AttributedAddPurchasingItemCapitalAssetEvent("", purDocument, asset)); if (rulePassed) { // grab system as well and attach asset number final CapitalAssetSystem system = assetItem.getPurchasingCapitalAssetSystem(); asset = assetItem.getAndResetNewPurchasingItemCapitalAssetLine(); asset.setCapitalAssetSystemIdentifier(system.getCapitalAssetSystemIdentifier()); if (capitalAssetSystemHasAssetItem(system, asset)) { - GlobalVariables.getMessageMap().putError(PurapConstants.CAPITAL_ASSET_TAB_ERRORS, PurapKeyConstants.ERROR_CAPITAL_ASSET_DUPLICATE_ASSET); + GlobalVariables.getMessageMap().putError(PurapConstants.CAPITAL_ASSET_TAB_ERRORS, + PurapKeyConstants.ERROR_CAPITAL_ASSET_DUPLICATE_ASSET); } else { system.getItemCapitalAssets().add(asset); } @@ -1149,7 +1143,7 @@ private boolean capitalAssetSystemHasAssetItem(final CapitalAssetSystem system, public ActionForward deleteItemCapitalAssetByDocument( final ActionMapping mapping, final ActionForm form, - final HttpServletRequest request, final HttpServletResponse response) throws Exception { + final HttpServletRequest request, final HttpServletResponse response) { final PurchasingFormBase purchasingForm = (PurchasingFormBase) form; final PurchasingDocument purDocument = (PurchasingDocument) purchasingForm.getDocument(); // get specific asset item @@ -1169,7 +1163,9 @@ public ActionForward deleteItemCapitalAssetByDocument( return mapping.findForward(KFSConstants.MAPPING_BASIC); } - public ActionForward deleteItemCapitalAssetByItem(final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, final HttpServletResponse response) throws Exception { + public ActionForward deleteItemCapitalAssetByItem( + final ActionMapping mapping, final ActionForm form, + final HttpServletRequest request, final HttpServletResponse response) { final PurchasingFormBase purchasingForm = (PurchasingFormBase) form; final PurchasingDocument purDocument = (PurchasingDocument) purchasingForm.getDocument(); // get specific asset item @@ -1188,12 +1184,15 @@ public ActionForward deleteItemCapitalAssetByItem(final ActionMapping mapping, f return mapping.findForward(KFSConstants.MAPPING_BASIC); } - public ActionForward addCapitalAssetLocationByDocument(final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, final HttpServletResponse response) throws Exception { + public ActionForward addCapitalAssetLocationByDocument( + final ActionMapping mapping, final ActionForm form, + final HttpServletRequest request, final HttpServletResponse response) { final PurchasingFormBase purchasingForm = (PurchasingFormBase) form; final CapitalAssetLocation location = purchasingForm.getAndResetNewPurchasingCapitalAssetLocationLine(); final PurchasingDocument purDocument = (PurchasingDocument) purchasingForm.getDocument(); - boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new AttributedAddPurchasingCapitalAssetLocationEvent("", purDocument, location)); + boolean rulePassed = SpringContext.getBean(KualiRuleService.class) + .applyRules(new AttributedAddPurchasingCapitalAssetLocationEvent("", purDocument, location)); rulePassed = rulePassed && SpringContext.getBean(PurchasingService.class).checkCapitalAssetLocation(location); if (rulePassed) { @@ -1206,44 +1205,53 @@ public ActionForward addCapitalAssetLocationByDocument(final ActionMapping mappi return mapping.findForward(KFSConstants.MAPPING_BASIC); } - public ActionForward addCapitalAssetLocationByItem(final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, final HttpServletResponse response) throws Exception { + public ActionForward addCapitalAssetLocationByItem( + final ActionMapping mapping, final ActionForm form, + final HttpServletRequest request, final HttpServletResponse response) { final PurchasingFormBase purchasingForm = (PurchasingFormBase) form; final PurchasingDocument purDocument = (PurchasingDocument) purchasingForm.getDocument(); - final CapitalAssetLocation location = purDocument.getPurchasingCapitalAssetItems().get(getSelectedLine(request)).getPurchasingCapitalAssetSystem().getNewPurchasingCapitalAssetLocationLine(); - boolean rulePassed = SpringContext.getBean(KualiRuleService.class).applyRules(new AttributedAddPurchasingCapitalAssetLocationEvent("", purDocument, location)); + final CapitalAssetLocation location = purDocument.getPurchasingCapitalAssetItems().get( + getSelectedLine(request)).getPurchasingCapitalAssetSystem().getNewPurchasingCapitalAssetLocationLine(); + final boolean rulePassed = SpringContext.getBean(KualiRuleService.class) + .applyRules(new AttributedAddPurchasingCapitalAssetLocationEvent("", purDocument, location)); if (rulePassed) { // get specific asset item and grab system as well and attach asset location - final PurchasingCapitalAssetItem assetItem = purDocument.getPurchasingCapitalAssetItems().get(getSelectedLine(request)); + final PurchasingCapitalAssetItem assetItem = purDocument.getPurchasingCapitalAssetItems() + .get(getSelectedLine(request)); final CapitalAssetSystem system = assetItem.getPurchasingCapitalAssetSystem(); location.setCapitalAssetSystemIdentifier(system.getCapitalAssetSystemIdentifier()); system.getCapitalAssetLocations().add(location); // now reset the location as all the rules are passed successfully - purDocument.getPurchasingCapitalAssetItems().get(getSelectedLine(request)).getPurchasingCapitalAssetSystem().resetNewPurchasingCapitalAssetLocationLine(); + purDocument.getPurchasingCapitalAssetItems().get(getSelectedLine(request)) + .getPurchasingCapitalAssetSystem().resetNewPurchasingCapitalAssetLocationLine(); } return mapping.findForward(KFSConstants.MAPPING_BASIC); } - public ActionForward deleteCapitalAssetLocationByDocument(final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, final HttpServletResponse response) throws Exception { + public ActionForward deleteCapitalAssetLocationByDocument( + final ActionMapping mapping, final ActionForm form, + final HttpServletRequest request, final HttpServletResponse response) { final PurchasingFormBase purchasingForm = (PurchasingFormBase) form; final PurchasingDocument purDocument = (PurchasingDocument) purchasingForm.getDocument(); final String fullParameter = (String) request.getAttribute(KFSConstants.METHOD_TO_CALL_ATTRIBUTE); - final String systemIndex = StringUtils.substringBetween(fullParameter, KFSConstants.METHOD_TO_CALL_PARM1_LEFT_DEL, KFSConstants.METHOD_TO_CALL_PARM1_RIGHT_DEL); - final String locationIndex = StringUtils.substringBetween(fullParameter, KFSConstants.METHOD_TO_CALL_PARM2_LEFT_DEL, KFSConstants.METHOD_TO_CALL_PARM2_RIGHT_DEL); + final String systemIndex = StringUtils.substringBetween(fullParameter, KFSConstants.METHOD_TO_CALL_PARM1_LEFT_DEL, + KFSConstants.METHOD_TO_CALL_PARM1_RIGHT_DEL); + final String locationIndex = StringUtils.substringBetween(fullParameter, KFSConstants.METHOD_TO_CALL_PARM2_LEFT_DEL, + KFSConstants.METHOD_TO_CALL_PARM2_RIGHT_DEL); // get specific asset item and grab system as well and attach asset number final CapitalAssetSystem system = purDocument.getPurchasingCapitalAssetSystems().get(Integer.parseInt(systemIndex)); system.getCapitalAssetLocations().remove(Integer.parseInt(locationIndex)); - return mapping.findForward(KFSConstants.MAPPING_BASIC); } public ActionForward deleteCapitalAssetLocationByItem( final ActionMapping mapping, final ActionForm form, - final HttpServletRequest request, final HttpServletResponse response) throws Exception { + final HttpServletRequest request, final HttpServletResponse response) { final PurchasingFormBase purchasingForm = (PurchasingFormBase) form; final PurchasingDocument purDocument = (PurchasingDocument) purchasingForm.getDocument(); @@ -1271,7 +1279,7 @@ public ActionForward deleteCapitalAssetLocationByItem( public ActionForward setupCAMSSystem( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PurchasingAccountsPayableFormBase purchasingForm = (PurchasingAccountsPayableFormBase) form; final PurchasingDocument document = (PurchasingDocument) purchasingForm.getDocument(); SpringContext.getBean(PurchasingService.class).setupCapitalAssetSystem(document); @@ -1280,7 +1288,7 @@ public ActionForward setupCAMSSystem( public ActionForward selectSystem( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PurchasingAccountsPayableFormBase purchasingForm = (PurchasingAccountsPayableFormBase) form; final PurchasingDocument document = (PurchasingDocument) purchasingForm.getDocument(); final String errorPath = PurapConstants.CAPITAL_ASSET_TAB_ERRORS; @@ -1358,7 +1366,7 @@ protected void saveDocumentNoValidationUsingClearErrorMap(final PurchasingDocume public ActionForward changeSystem( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PurchasingAccountsPayableFormBase purchasingForm = (PurchasingAccountsPayableFormBase) form; final PurchasingDocument document = (PurchasingDocument) purchasingForm.getDocument(); final Object question = request.getParameter(PurapConstants.QUESTION_INDEX); @@ -1425,18 +1433,16 @@ public ActionForward changeSystem( public ActionForward updateCamsView( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PurchasingAccountsPayableFormBase purchasingForm = (PurchasingAccountsPayableFormBase) form; final PurchasingDocument document = (PurchasingDocument) purchasingForm.getDocument(); - SpringContext.getBean(PurchasingService.class).setupCapitalAssetItems(document); - return mapping.findForward(KFSConstants.MAPPING_BASIC); } public ActionForward setManufacturerFromVendorByDocument( final ActionMapping mapping, final ActionForm form, - final HttpServletRequest request, final HttpServletResponse response) throws Exception { + final HttpServletRequest request, final HttpServletResponse response) { final PurchasingAccountsPayableFormBase purchasingForm = (PurchasingAccountsPayableFormBase) form; final PurchasingDocument document = (PurchasingDocument) purchasingForm.getDocument(); @@ -1456,7 +1462,7 @@ public ActionForward setManufacturerFromVendorByDocument( public ActionForward setManufacturerFromVendorByItem( final ActionMapping mapping, final ActionForm form, - final HttpServletRequest request, final HttpServletResponse response) throws Exception { + final HttpServletRequest request, final HttpServletResponse response) { final PurchasingAccountsPayableFormBase purchasingForm = (PurchasingAccountsPayableFormBase) form; final PurchasingDocument document = (PurchasingDocument) purchasingForm.getDocument(); @@ -1476,7 +1482,7 @@ public ActionForward setManufacturerFromVendorByItem( public ActionForward selectNotCurrentYearByDocument( final ActionMapping mapping, final ActionForm form, - final HttpServletRequest request, final HttpServletResponse response) throws Exception { + final HttpServletRequest request, final HttpServletResponse response) { final PurchasingAccountsPayableFormBase purchasingForm = (PurchasingAccountsPayableFormBase) form; final PurchasingDocument document = (PurchasingDocument) purchasingForm.getDocument(); @@ -1492,7 +1498,7 @@ public ActionForward selectNotCurrentYearByDocument( public ActionForward selectNotCurrentYearByItem( final ActionMapping mapping, final ActionForm form, - final HttpServletRequest request, final HttpServletResponse response) throws Exception { + final HttpServletRequest request, final HttpServletResponse response) { final PurchasingAccountsPayableFormBase purchasingForm = (PurchasingAccountsPayableFormBase) form; final PurchasingDocument document = (PurchasingDocument) purchasingForm.getDocument(); @@ -1509,7 +1515,7 @@ public ActionForward selectNotCurrentYearByItem( public ActionForward clearNotCurrentYearByDocument( final ActionMapping mapping, final ActionForm form, - final HttpServletRequest request, final HttpServletResponse response) throws Exception { + final HttpServletRequest request, final HttpServletResponse response) { final PurchasingAccountsPayableFormBase purchasingForm = (PurchasingAccountsPayableFormBase) form; final PurchasingDocument document = (PurchasingDocument) purchasingForm.getDocument(); @@ -1524,7 +1530,7 @@ public ActionForward clearNotCurrentYearByDocument( public ActionForward clearNotCurrentYearByItem( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PurchasingAccountsPayableFormBase purchasingForm = (PurchasingAccountsPayableFormBase) form; final PurchasingDocument document = (PurchasingDocument) purchasingForm.getDocument(); diff --git a/src/main/java/org/kuali/kfs/sys/businessobject/lookup/BatchFileLookupableHelperServiceImpl.java b/src/main/java/org/kuali/kfs/sys/businessobject/lookup/BatchFileLookupableHelperServiceImpl.java index 544536a0f9..fc9cc00fb0 100644 --- a/src/main/java/org/kuali/kfs/sys/businessobject/lookup/BatchFileLookupableHelperServiceImpl.java +++ b/src/main/java/org/kuali/kfs/sys/businessobject/lookup/BatchFileLookupableHelperServiceImpl.java @@ -37,6 +37,7 @@ import org.kuali.kfs.sys.batch.BatchFileUtils; import org.kuali.kfs.sys.batch.service.BatchFileAdminAuthorizationService; import org.kuali.kfs.sys.util.KfsDateUtils; +import org.kuali.kfs.sys.web.struts.KualiBatchFileAdminAction; import org.kuali.kfs.core.api.datetime.DateTimeService; import org.kuali.kfs.krad.bo.BusinessObject; @@ -51,6 +52,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Properties; +import java.util.stream.Collectors; /* * Cornell Customization: Add old batch file lookup to support lookups on Create Disencumbrance page. This should be removed once the new batch file lookup will support return from lookup. @@ -83,7 +85,7 @@ public List getSearchResults(Map field } BatchFileFinder finder = new BatchFileFinder(results, filter); - List rootDirectories = BatchFileUtils.retrieveBatchFileLookupRootDirectories(); + List rootDirectories = BatchFileUtils.retrieveBatchFileLookupRootDirectories().stream().map(Path::toFile).collect(Collectors.toList()); finder.find(rootDirectories); return results; @@ -301,7 +303,7 @@ public void validateSearchParameters(Map fieldValues) { if (selectedPaths != null) { for (String selectedPath : selectedPaths) { String resolvedPath = BatchFileUtils.resolvePathToAbsolutePath(selectedPath); - if (!BatchFileUtils.isDirectoryAccessible(resolvedPath)) { + if (!KualiBatchFileAdminAction.isDirectoryAccessible(resolvedPath)) { throw new RuntimeException("Can't access path " + selectedPath); } } diff --git a/src/main/java/org/kuali/kfs/sys/web/struts/KualiBatchFileAdminAction.java b/src/main/java/org/kuali/kfs/sys/web/struts/KualiBatchFileAdminAction.java new file mode 100644 index 0000000000..b66287c439 --- /dev/null +++ b/src/main/java/org/kuali/kfs/sys/web/struts/KualiBatchFileAdminAction.java @@ -0,0 +1,207 @@ +/* + * The Kuali Financial System, a comprehensive financial management system for higher education. + * + * Copyright 2005-2024 Kuali, Inc. + * + * 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 . + */ +package org.kuali.kfs.sys.web.struts; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.struts.action.ActionForm; +import org.apache.struts.action.ActionForward; +import org.apache.struts.action.ActionMapping; +import org.kuali.kfs.core.api.config.property.ConfigurationService; +import org.kuali.kfs.core.api.util.KeyValue; +import org.kuali.kfs.datadictionary.legacy.DataDictionaryService; +import org.kuali.kfs.kns.datadictionary.control.ControlDefinition; +import org.kuali.kfs.kns.question.ConfirmationQuestion; +import org.kuali.kfs.kns.service.KNSServiceLocator; +import org.kuali.kfs.kns.web.struts.action.KualiAction; +import org.kuali.kfs.krad.exception.AuthorizationException; +import org.kuali.kfs.krad.keyvalues.KeyValuesFinder; +import org.kuali.kfs.krad.util.GlobalVariables; +import org.kuali.kfs.krad.util.KRADConstants; +import org.kuali.kfs.sys.KFSConstants; +import org.kuali.kfs.sys.KFSKeyConstants; +import org.kuali.kfs.sys.batch.BatchFile; +import org.kuali.kfs.sys.batch.BatchFileUtils; +import org.kuali.kfs.sys.batch.service.BatchFileAdminAuthorizationService; +import org.kuali.kfs.sys.context.SpringContext; +import org.kuali.kfs.sys.service.FileStorageService; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.InputStream; +import java.nio.file.Path; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; + +// CU customization to increase method visibility +public class KualiBatchFileAdminAction extends KualiAction { + + public ActionForward download( + final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, + final HttpServletResponse response) throws Exception { + final KualiBatchFileAdminForm fileAdminForm = (KualiBatchFileAdminForm) form; + final String filePath = BatchFileUtils.resolvePathToAbsolutePath(fileAdminForm.getFilePath()); + final FileStorageService fileStorageService = SpringContext.getBean(FileStorageService.class); + final String directoryName = StringUtils.substringBeforeLast(filePath, fileStorageService.separator()); + final String fileName = StringUtils.substringAfterLast(filePath, fileStorageService.separator()); + + if (!fileStorageService.fileExists(filePath)) { + throw new RuntimeException("Error: non-existent file or directory provided"); + } + if (!isDirectoryAccessible(directoryName)) { + throw new RuntimeException("Error: inaccessible directory provided"); + } + + // TODO: Eliminate use of File class altogether, once BatchFile class is refactored. + final File file = new File(filePath).getAbsoluteFile(); + final BatchFile batchFile = new BatchFile(file); + if (!SpringContext.getBean(BatchFileAdminAuthorizationService.class).canDownload(batchFile, + GlobalVariables.getUserSession().getPerson())) { + logFileAction(filePath, "DOWNLOAD", false); + throw new RuntimeException("Error: not authorized to download file"); + } + + response.setContentType("application/octet-stream"); + response.setHeader("Content-disposition", "attachment; filename=" + fileName); + response.setHeader("Expires", "0"); + response.setHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0"); + response.setHeader("Pragma", "public"); + response.setContentLength((int) fileStorageService.getFileLength(filePath)); + + logFileAction(filePath, "DOWNLOAD", true); + try (InputStream fis = fileStorageService.getFileStream(filePath)) { + IOUtils.copy(fis, response.getOutputStream()); + } + response.getOutputStream().flush(); + return null; + } + + public ActionForward delete( + final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, + final HttpServletResponse response) { + final KualiBatchFileAdminForm fileAdminForm = (KualiBatchFileAdminForm) form; + final String filePath = BatchFileUtils.resolvePathToAbsolutePath(fileAdminForm.getFilePath()); + final FileStorageService fileStorageService = SpringContext.getBean(FileStorageService.class); + final String directoryName = StringUtils.substringBeforeLast(filePath, fileStorageService.separator()); + + final ConfigurationService kualiConfigurationService = SpringContext.getBean(ConfigurationService.class); + + if (!fileStorageService.fileExists(filePath)) { + throw new RuntimeException("Error: non-existent file or directory provided"); + } + if (!isDirectoryAccessible(directoryName)) { + throw new RuntimeException("Error: inaccessible directory provided"); + } + + // TODO: Eliminate use of File class altogether, once BatchFile class is refactored. + final File file = new File(filePath).getAbsoluteFile(); + final BatchFile batchFile = new BatchFile(file); + if (!SpringContext.getBean(BatchFileAdminAuthorizationService.class).canDelete(batchFile, + GlobalVariables.getUserSession().getPerson())) { + logFileAction(filePath, "DELETE", false); + throw new RuntimeException("Error: not authorized to delete file"); + } + + final String displayFileName = BatchFileUtils.pathRelativeToRootDirectory(filePath); + + final Object question = request.getParameter(KFSConstants.QUESTION_INST_ATTRIBUTE_NAME); + if (question == null) { + String questionText = kualiConfigurationService.getPropertyValueAsString( + KFSKeyConstants.QUESTION_BATCH_FILE_ADMIN_DELETE_CONFIRM); + questionText = MessageFormat.format(questionText, displayFileName); + return performQuestionWithoutInput(mapping, fileAdminForm, request, response, "confirmDelete", + questionText, + KRADConstants.CONFIRMATION_QUESTION, "delete", fileAdminForm.getFilePath()); + } else { + final Object buttonClicked = request.getParameter(KFSConstants.QUESTION_CLICKED_BUTTON); + if ("confirmDelete".equals(question)) { + String status = null; + if (ConfirmationQuestion.YES.equals(buttonClicked)) { + try { + logFileAction(filePath, "DELETE", true); + fileStorageService.delete(filePath); + status = kualiConfigurationService.getPropertyValueAsString( + KFSKeyConstants.MESSAGE_BATCH_FILE_ADMIN_DELETE_SUCCESSFUL); + status = MessageFormat.format(status, displayFileName); + } catch (final Exception e) { + status = kualiConfigurationService.getPropertyValueAsString( + KFSKeyConstants.MESSAGE_BATCH_FILE_ADMIN_DELETE_ERROR); + status = MessageFormat.format(status, displayFileName); + } + } else if (ConfirmationQuestion.NO.equals(buttonClicked)) { + status = kualiConfigurationService.getPropertyValueAsString( + KFSKeyConstants.MESSAGE_BATCH_FILE_ADMIN_DELETE_CANCELLED); + status = MessageFormat.format(status, displayFileName); + } + if (status != null) { + request.setAttribute("status", status); + return mapping.findForward(KFSConstants.MAPPING_BASIC); + } + } + throw new RuntimeException("Unrecognized question: " + question + " or response: " + buttonClicked); + } + } + + @Override + protected void checkAuthorization(final ActionForm form, final String methodToCall) throws AuthorizationException { + // do nothing... authorization is integrated into action handler + } + + protected void logFileAction(final String filePath, final String action, final boolean granted) { + final StringBuilder buf = new StringBuilder(300); + buf.append(action).append(",").append(granted ? "SUCCESS" : "DENY").append(",").append(filePath); + KNSServiceLocator.getSecurityLoggingService().logCustomString(buf.toString()); + } + + // CU customization to increase method visibility + public static boolean isDirectoryAccessible(final String directory) { + List pathNames = null; + + final ControlDefinition controlDefinition = SpringContext.getBean(DataDictionaryService.class) + .getAttributeControlDefinition("BatchFile", "path"); + final KeyValuesFinder keyValuesFinder = controlDefinition.getValuesFinder(); + if (keyValuesFinder != null) { + pathNames = new ArrayList<>(); + + final List keyValues = keyValuesFinder.getKeyValues(); + for (final KeyValue keyValue : keyValues) { + pathNames.add(new File(BatchFileUtils.resolvePathToAbsolutePath(keyValue.getKey())).getAbsolutePath()); + } + } + + final File directoryAbsolute = new File(directory).getAbsoluteFile(); + final String directoryAbsolutePath = directoryAbsolute.getAbsolutePath(); + if (pathNames != null) { + if (!pathNames.contains(directoryAbsolutePath)) { + return false; + } + } + + final List rootDirectories = BatchFileUtils.retrieveBatchFileLookupRootDirectories(); + for (final Path rootDirectory : rootDirectories) { + if (BatchFileUtils.isSuperDirectoryOf(rootDirectory, directoryAbsolute.toPath())) { + return true; + } + } + return false; + } + +} From d16bd09152e8ac31465c760cc3b1a49e6ec121da Mon Sep 17 00:00:00 2001 From: Daniela Balmus Date: Fri, 6 Feb 2026 09:58:05 +0000 Subject: [PATCH 04/12] KFSPTS-36246 get things to compile --- .../struts/YearEndJournalVoucherAction.java | 2 +- ...uContractsGrantsInvoiceDocumentAction.java | 4 ++-- .../impl/CuAssetDepreciationServiceImpl.java | 2 +- ...FileEnterpriseFeederHelperServiceImpl.java | 7 +++++- .../impl/CuB2BShoppingServiceImpl.java | 2 +- .../service/impl/CuCreditMemoServiceImpl.java | 4 ++-- .../web/struts/CuRequisitionAction.java | 6 ++--- .../CuPurchaseOrderActionListAttribute.java | 2 +- .../batch/AwardTranslationDefinition.java | 2 +- .../service/impl/RassUpdateServiceImpl.java | 3 ++- ...uBatchFileLookupableHelperServiceImpl.java | 4 ++-- .../DefaultExceptionServiceImpl.java | 2 +- .../BatchFileLookupableHelperServiceImpl.java | 15 ++++++------ ...actCreateCollectorFileServiceImplTest.java | 5 ---- .../service/impl/RassServiceImplTest.java | 2 +- .../xml/fixture/RassXmlAwardEntryFixture.java | 4 ++-- ...chFileLookupableHelperServiceImplTest.java | 24 ++++++++++--------- 17 files changed, 47 insertions(+), 43 deletions(-) diff --git a/src/main/java/edu/cornell/kfs/fp/document/web/struts/YearEndJournalVoucherAction.java b/src/main/java/edu/cornell/kfs/fp/document/web/struts/YearEndJournalVoucherAction.java index 4321e58649..36778bb21d 100644 --- a/src/main/java/edu/cornell/kfs/fp/document/web/struts/YearEndJournalVoucherAction.java +++ b/src/main/java/edu/cornell/kfs/fp/document/web/struts/YearEndJournalVoucherAction.java @@ -17,7 +17,7 @@ public YearEndJournalVoucherAction() { @Override protected ActionForward processRouteOutOfBalanceDocumentConfirmationQuestion( ActionMapping mapping, ActionForm form, HttpServletRequest request, - HttpServletResponse response) throws Exception { + HttpServletResponse response) { // we return null as we don't want pre rule to check if doc is out of balance, BR is going to check if doc is out of balance return null; } diff --git a/src/main/java/edu/cornell/kfs/module/ar/document/web/struts/CuContractsGrantsInvoiceDocumentAction.java b/src/main/java/edu/cornell/kfs/module/ar/document/web/struts/CuContractsGrantsInvoiceDocumentAction.java index f2ee601885..757ef1b2e7 100644 --- a/src/main/java/edu/cornell/kfs/module/ar/document/web/struts/CuContractsGrantsInvoiceDocumentAction.java +++ b/src/main/java/edu/cornell/kfs/module/ar/document/web/struts/CuContractsGrantsInvoiceDocumentAction.java @@ -43,7 +43,7 @@ public class CuContractsGrantsInvoiceDocumentAction extends ContractsGrantsInvoi @Override public ActionForward prorateBill( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final ContractsGrantsInvoiceDocumentForm contractsGrantsInvoiceDocumentForm = (ContractsGrantsInvoiceDocumentForm) form; KualiDecimal budgetTotalAmount = findAwardBudgetTotal(contractsGrantsInvoiceDocumentForm); @@ -208,7 +208,7 @@ protected ActionForward promptForFinalBillConfirmation(ActionMapping mapping, Ac protected ActionForward promptForSuspensionCategories( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, final HttpServletResponse response, - final ContractsGrantsInvoiceDocument contractsGrantsInvoiceDocument, final String caller) throws Exception { + final ContractsGrantsInvoiceDocument contractsGrantsInvoiceDocument, final String caller) { ActionForward forward = null; if (CollectionUtils.size(contractsGrantsInvoiceDocument.getInvoiceSuspensionCategories()) > 0) { diff --git a/src/main/java/edu/cornell/kfs/module/cam/batch/service/impl/CuAssetDepreciationServiceImpl.java b/src/main/java/edu/cornell/kfs/module/cam/batch/service/impl/CuAssetDepreciationServiceImpl.java index 73fdfc790f..0bded05ccd 100644 --- a/src/main/java/edu/cornell/kfs/module/cam/batch/service/impl/CuAssetDepreciationServiceImpl.java +++ b/src/main/java/edu/cornell/kfs/module/cam/batch/service/impl/CuAssetDepreciationServiceImpl.java @@ -29,7 +29,7 @@ public class CuAssetDepreciationServiceImpl extends AssetDepreciationServiceImpl private static final Logger LOG = LogManager.getLogger(); @Override - protected boolean runAssetDepreciation() throws ParseException { + protected boolean runAssetDepreciation() { return true; } diff --git a/src/main/java/edu/cornell/kfs/module/ld/batch/service/impl/CuFileEnterpriseFeederHelperServiceImpl.java b/src/main/java/edu/cornell/kfs/module/ld/batch/service/impl/CuFileEnterpriseFeederHelperServiceImpl.java index b80bd22233..4aeeaf06b4 100644 --- a/src/main/java/edu/cornell/kfs/module/ld/batch/service/impl/CuFileEnterpriseFeederHelperServiceImpl.java +++ b/src/main/java/edu/cornell/kfs/module/ld/batch/service/impl/CuFileEnterpriseFeederHelperServiceImpl.java @@ -11,6 +11,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -34,6 +35,7 @@ import org.kuali.kfs.module.ld.businessobject.LaborOriginEntry; import org.kuali.kfs.module.ld.businessobject.PositionObjectBenefit; import org.kuali.kfs.module.ld.report.EnterpriseFeederReportData; +import org.kuali.kfs.module.ld.util.BenefitOffsetKey; import org.kuali.kfs.module.ld.util.LaborOriginEntryFileIterator; import org.kuali.kfs.sys.KFSConstants; import org.kuali.kfs.sys.KFSPropertyConstants; @@ -89,6 +91,9 @@ public void feedOnFile( if (reconciliationProcessSucceeded(errorMessages)) { String line; int count = 0; + + // placeholder of all generated offset entries + final Map> salaryBenefitOffsets = new LinkedHashMap<>(); Collection offsetDocTypes = parameterService.getParameterValuesAsString( LaborEnterpriseFeedStep.class, LaborParameterConstants.BENEFITS_DOCUMENT_TYPES); @@ -108,7 +113,7 @@ public void feedOnFile( feederReportData.incrementNumberOfRecordsWritten(); feederReportData.addToTotalAmountWritten(tempEntry.getTransactionLedgerEntryAmount()); - final List benefitEntries = generateBenefits(tempEntry, errorStatisticsReport, feederReportData); + final List benefitEntries = generateBenefits(tempEntry, errorStatisticsReport, feederReportData, salaryBenefitOffsets); KualiDecimal benefitTotal = new KualiDecimal (0); KualiDecimal offsetTotal = new KualiDecimal (0); diff --git a/src/main/java/edu/cornell/kfs/module/purap/document/service/impl/CuB2BShoppingServiceImpl.java b/src/main/java/edu/cornell/kfs/module/purap/document/service/impl/CuB2BShoppingServiceImpl.java index 3423165304..dd47fb05d5 100644 --- a/src/main/java/edu/cornell/kfs/module/purap/document/service/impl/CuB2BShoppingServiceImpl.java +++ b/src/main/java/edu/cornell/kfs/module/purap/document/service/impl/CuB2BShoppingServiceImpl.java @@ -402,7 +402,7 @@ protected RequisitionItem createRequisitionItem( reqItem.setItemDescription(reqItem.getItemDescription().substring(0, requisitionItemDescriptionMaxLength)); } - final boolean commCodeParam = parameterService.getParameterValueAsBoolean(RequisitionDocument.class, PurapParameterConstants.ENABLE_DEFAULT_VENDOR_COMMODITY_CODE_IND); + final boolean commCodeParam = parameterService.getParameterValueAsBoolean(RequisitionDocument.class, PurapParameterConstants.COMMODITY_CODE_TO_LINE_ITEM_IND); if (commCodeParam) { if (reqItem.getCommodityCode() != null && !reqItem.getCommodityCode().isActive() && StringUtils.isNotBlank(defaultCommodityCode)) { diff --git a/src/main/java/edu/cornell/kfs/module/purap/document/service/impl/CuCreditMemoServiceImpl.java b/src/main/java/edu/cornell/kfs/module/purap/document/service/impl/CuCreditMemoServiceImpl.java index 8646f8e1aa..0435209ece 100644 --- a/src/main/java/edu/cornell/kfs/module/purap/document/service/impl/CuCreditMemoServiceImpl.java +++ b/src/main/java/edu/cornell/kfs/module/purap/document/service/impl/CuCreditMemoServiceImpl.java @@ -28,7 +28,7 @@ public class CuCreditMemoServiceImpl extends CreditMemoServiceImpl { private CUPaymentMethodGeneralLedgerPendingEntryService paymentMethodGeneralLedgerPendingEntryService; @Override - public VendorCreditMemoDocument addHoldOnCreditMemo(final VendorCreditMemoDocument cmDocument, final String note) throws Exception { + public VendorCreditMemoDocument addHoldOnCreditMemo(final VendorCreditMemoDocument cmDocument, final String note) { // save the note final Note noteObj = documentService.createNoteFromDocument(cmDocument, note); cmDocument.addNote(noteObj); @@ -56,7 +56,7 @@ public VendorCreditMemoDocument addHoldOnCreditMemo(final VendorCreditMemoDocume * java.lang.String) */ @Override - public VendorCreditMemoDocument removeHoldOnCreditMemo(final VendorCreditMemoDocument cmDocument, final String note) throws Exception { + public VendorCreditMemoDocument removeHoldOnCreditMemo(final VendorCreditMemoDocument cmDocument, final String note) { // save the note final Note noteObj = documentService.createNoteFromDocument(cmDocument, note); cmDocument.addNote(noteObj); diff --git a/src/main/java/edu/cornell/kfs/module/purap/document/web/struts/CuRequisitionAction.java b/src/main/java/edu/cornell/kfs/module/purap/document/web/struts/CuRequisitionAction.java index cb2209de40..104d2d885b 100644 --- a/src/main/java/edu/cornell/kfs/module/purap/document/web/struts/CuRequisitionAction.java +++ b/src/main/java/edu/cornell/kfs/module/purap/document/web/struts/CuRequisitionAction.java @@ -54,7 +54,7 @@ public class CuRequisitionAction extends RequisitionAction { @Override public ActionForward addItem( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PurchasingFormBase purchasingForm = (PurchasingFormBase) form; PurApItem item = purchasingForm.getNewPurchasingItemLine(); final RequisitionItem requisitionItem = (RequisitionItem) item; @@ -62,7 +62,7 @@ public ActionForward addItem( if (StringUtils.isBlank(requisitionItem.getPurchasingCommodityCode())) { final boolean commCodeParam = SpringContext.getBean(ParameterService.class).getParameterValueAsBoolean( - CuRequisitionDocument.class, PurapParameterConstants.ENABLE_DEFAULT_VENDOR_COMMODITY_CODE_IND); + CuRequisitionDocument.class, PurapParameterConstants.COMMODITY_CODE_TO_LINE_ITEM_IND); if (commCodeParam && purchasingForm instanceof RequisitionForm) { final CuRequisitionDocument reqs = (CuRequisitionDocument) purchasingForm.getDocument(); @@ -95,7 +95,7 @@ public ActionForward addItem( public ActionForward clearVendor( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PurchasingFormBase baseForm = (PurchasingFormBase) form; final CuRequisitionDocument document = (CuRequisitionDocument) baseForm.getDocument(); diff --git a/src/main/java/edu/cornell/kfs/module/purap/document/workflow/CuPurchaseOrderActionListAttribute.java b/src/main/java/edu/cornell/kfs/module/purap/document/workflow/CuPurchaseOrderActionListAttribute.java index bc7b0c9774..79016a18ba 100644 --- a/src/main/java/edu/cornell/kfs/module/purap/document/workflow/CuPurchaseOrderActionListAttribute.java +++ b/src/main/java/edu/cornell/kfs/module/purap/document/workflow/CuPurchaseOrderActionListAttribute.java @@ -9,7 +9,7 @@ public class CuPurchaseOrderActionListAttribute extends PurchaseOrderActionListAttribute { @Override - public ActionSet getLegalActions(final String principalId, final ActionItem actionItem) throws Exception { + public ActionSet getLegalActions(final String principalId, final ActionItem actionItem) { final List actionSetList = new ArrayList<>(); actionSetList.add(KewApiConstants.ACTION_TAKEN_FYI_CD); return new ActionSet(actionSetList); diff --git a/src/main/java/edu/cornell/kfs/rass/batch/AwardTranslationDefinition.java b/src/main/java/edu/cornell/kfs/rass/batch/AwardTranslationDefinition.java index f1f0ad1d2e..ae1e9dd167 100644 --- a/src/main/java/edu/cornell/kfs/rass/batch/AwardTranslationDefinition.java +++ b/src/main/java/edu/cornell/kfs/rass/batch/AwardTranslationDefinition.java @@ -77,7 +77,7 @@ public void processCustomTranslationForBusinessObjectCreate( RassXmlAwardEntry xmlAward, Award newAward) { String proposalAwardTypeCode = parameterService.getParameterValueAsString( RassStep.class, RassConstants.RASS_DEFAULT_PROPOSAL_AWARD_TYPE_PARAMETER); - newAward.setProposalAwardTypeCode(proposalAwardTypeCode); + newAward.setAwardTypeCode(proposalAwardTypeCode); newAward.setAwardEntryDate(dateTimeService.getCurrentSqlDate()); newAward.getAwardAccounts().add(createDefaultAwardAccount(xmlAward)); addPrimaryFundManager(xmlAward, newAward); diff --git a/src/main/java/edu/cornell/kfs/rass/batch/service/impl/RassUpdateServiceImpl.java b/src/main/java/edu/cornell/kfs/rass/batch/service/impl/RassUpdateServiceImpl.java index 4db59d808f..7cf1ca893d 100644 --- a/src/main/java/edu/cornell/kfs/rass/batch/service/impl/RassUpdateServiceImpl.java +++ b/src/main/java/edu/cornell/kfs/rass/batch/service/impl/RassUpdateServiceImpl.java @@ -34,6 +34,7 @@ import org.kuali.kfs.sys.businessobject.DocumentHeader; import org.kuali.kfs.core.api.config.property.ConfigurationService; import org.kuali.kfs.core.api.mo.common.active.MutableInactivatable; +import org.kuali.kfs.core.web.format.FormatException; import org.kuali.kfs.kew.api.KewApiConstants; import org.kuali.kfs.kew.api.exception.WorkflowException; import org.kuali.kfs.kew.routeheader.service.RouteHeaderService; @@ -114,7 +115,7 @@ public void waitForRemainingGeneratedDocumentsToFinish(PendingDocumentTracker do protected void materializeProxiedCollectionsOnExistingObject(Object existingObject) { try { ObjectUtils.materializeUpdateableCollections(existingObject); - } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + } catch (FormatException e) { throw new RuntimeException(e); } } diff --git a/src/main/java/edu/cornell/kfs/sys/businessobject/lookup/CuBatchFileLookupableHelperServiceImpl.java b/src/main/java/edu/cornell/kfs/sys/businessobject/lookup/CuBatchFileLookupableHelperServiceImpl.java index d859016340..d380bb354b 100644 --- a/src/main/java/edu/cornell/kfs/sys/businessobject/lookup/CuBatchFileLookupableHelperServiceImpl.java +++ b/src/main/java/edu/cornell/kfs/sys/businessobject/lookup/CuBatchFileLookupableHelperServiceImpl.java @@ -35,12 +35,12 @@ public List getSearchResults(Map field } BatchFileFinder finder = new BatchFileFinder(results, filter); - List selectedDirectories = getSelectedDirectories(getSelectedPaths()); + List selectedDirectories = getSelectedDirectories(getSelectedPaths()); if (selectedDirectories.isEmpty()) { List rootDirectories = retrieveRootDirectories().stream().map(Path::toFile).collect(Collectors.toList()); finder.find(rootDirectories); } else { - finder.find(selectedDirectories); + finder.find(selectedDirectories.stream().map(Path::toFile).collect(Collectors.toList())); } return results; diff --git a/src/main/java/org/kuali/kfs/ksb/messaging/exceptionhandling/DefaultExceptionServiceImpl.java b/src/main/java/org/kuali/kfs/ksb/messaging/exceptionhandling/DefaultExceptionServiceImpl.java index cd47e8c417..e2da8f62b5 100644 --- a/src/main/java/org/kuali/kfs/ksb/messaging/exceptionhandling/DefaultExceptionServiceImpl.java +++ b/src/main/java/org/kuali/kfs/ksb/messaging/exceptionhandling/DefaultExceptionServiceImpl.java @@ -73,7 +73,7 @@ public void placeInExceptionRouting(final Throwable throwable, final PersistedMe } @Override - public void placeInExceptionRoutingLastDitchEffort(final Throwable throwable, final PersistedMessage message) throws Exception { + public void placeInExceptionRoutingLastDitchEffort(final Throwable throwable, final PersistedMessage message) { LOG.error( "Exception caught processing message {} {}: {}", message::getRouteQueueId, diff --git a/src/main/java/org/kuali/kfs/sys/businessobject/lookup/BatchFileLookupableHelperServiceImpl.java b/src/main/java/org/kuali/kfs/sys/businessobject/lookup/BatchFileLookupableHelperServiceImpl.java index fc9cc00fb0..42a420256b 100644 --- a/src/main/java/org/kuali/kfs/sys/businessobject/lookup/BatchFileLookupableHelperServiceImpl.java +++ b/src/main/java/org/kuali/kfs/sys/businessobject/lookup/BatchFileLookupableHelperServiceImpl.java @@ -44,6 +44,7 @@ import java.io.File; import java.io.IOException; import java.nio.file.Path; +import java.nio.file.Paths; import java.text.ParseException; import java.util.ArrayList; import java.util.Collection; @@ -92,13 +93,13 @@ public List getSearchResults(Map field } protected IOFileFilter getPathBasedFileFilter() { - List selectedFiles = getSelectedDirectories(getSelectedPaths()); + List selectedFiles = getSelectedDirectories(getSelectedPaths()); if (selectedFiles.isEmpty()) { return null; } IOFileFilter fileFilter = null; - for (File selectedFile : selectedFiles) { - IOFileFilter subFilter = new SubDirectoryFileFilter(selectedFile); + for (Path selectedFile : selectedFiles) { + IOFileFilter subFilter = new SubDirectoryFileFilter(selectedFile.toFile()); if (fileFilter == null) { fileFilter = subFilter; } else { @@ -142,12 +143,12 @@ protected IOFileFilter getLastModifiedDateBasedFilter(String lastModifiedDatePat throw new RuntimeException("Unable to perform search using last modified date " + lastModifiedDatePattern); } - protected List getSelectedDirectories(String[] selectedPaths) { - List directories = new ArrayList(); + protected List getSelectedDirectories(String[] selectedPaths) { + List directories = new ArrayList(); if (selectedPaths != null) { for (String selectedPath : selectedPaths) { - File directory = new File(BatchFileUtils.resolvePathToAbsolutePath(selectedPath)); - if (!directory.exists()) { + Path directory = Paths.get(BatchFileUtils.resolvePathToAbsolutePath(selectedPath)).toAbsolutePath(); + if (!directory.toFile().exists()) { throw new RuntimeException("Non existent directory " + BatchFileUtils.resolvePathToAbsolutePath(selectedPath)); } directories.add(directory); diff --git a/src/test/java/edu/cornell/kfs/concur/batch/service/impl/ConcurStandardAccountingExtractCreateCollectorFileServiceImplTest.java b/src/test/java/edu/cornell/kfs/concur/batch/service/impl/ConcurStandardAccountingExtractCreateCollectorFileServiceImplTest.java index 6971c66da6..b4697f64d5 100644 --- a/src/test/java/edu/cornell/kfs/concur/batch/service/impl/ConcurStandardAccountingExtractCreateCollectorFileServiceImplTest.java +++ b/src/test/java/edu/cornell/kfs/concur/batch/service/impl/ConcurStandardAccountingExtractCreateCollectorFileServiceImplTest.java @@ -375,11 +375,6 @@ protected ConcurStandardAccountingExtractCollectorBatchBuilder createBatchBuilde */ public static class TestBatchFileLookupSearchServiceImpl extends BatchFileSearchService { private static final long serialVersionUID = 1L; - - @Override - protected List getDirectoriesToSearch(List selectedPaths) { - return Collections.singletonList(new File(COLLECTOR_OUTPUT_DIRECTORY_PATH).getAbsoluteFile()); - } } } diff --git a/src/test/java/edu/cornell/kfs/rass/batch/service/impl/RassServiceImplTest.java b/src/test/java/edu/cornell/kfs/rass/batch/service/impl/RassServiceImplTest.java index 2f6001ceee..e8270934ef 100644 --- a/src/test/java/edu/cornell/kfs/rass/batch/service/impl/RassServiceImplTest.java +++ b/src/test/java/edu/cornell/kfs/rass/batch/service/impl/RassServiceImplTest.java @@ -951,7 +951,7 @@ private void assertAgencyWasUpdatedAsExpected(RassXmlAgencyEntryFixture expected private void assertAwardWasUpdatedAsExpected(RassXmlAwardEntryFixture expectedAward, Award actualAward, int i) { assertEquals("Wrong proposal/award type at index " + i, - RassTestConstants.DEFAULT_PROPOSAL_AWARD_TYPE, actualAward.getProposalAwardTypeCode()); + RassTestConstants.DEFAULT_PROPOSAL_AWARD_TYPE, actualAward.getAwardTypeCode()); assertEqualsOrBothBlank("Wrong proposal number at index " + i, expectedAward.proposalNumber, actualAward.getProposalNumber()); assertEqualsOrBothBlank("Wrong award status at index " + i, expectedAward.status, actualAward.getAwardStatusCode()); assertEqualsOrBothBlank("Wrong agency number at index " + i, expectedAward.agencyNumber, actualAward.getAgencyNumber()); diff --git a/src/test/java/edu/cornell/kfs/rass/batch/xml/fixture/RassXmlAwardEntryFixture.java b/src/test/java/edu/cornell/kfs/rass/batch/xml/fixture/RassXmlAwardEntryFixture.java index 7ecbdd3b0a..897c3cefa7 100644 --- a/src/test/java/edu/cornell/kfs/rass/batch/xml/fixture/RassXmlAwardEntryFixture.java +++ b/src/test/java/edu/cornell/kfs/rass/batch/xml/fixture/RassXmlAwardEntryFixture.java @@ -283,7 +283,7 @@ public RassXmlAwardEntry toRassXmlAwardEntry() { public Proposal toProposal() { Proposal proposal = new Proposal(); - proposal.setProposalAwardTypeCode(RassTestConstants.DEFAULT_PROPOSAL_AWARD_TYPE); + proposal.setAwardTypeCode(RassTestConstants.DEFAULT_PROPOSAL_AWARD_TYPE); proposal.setProposalNumber(defaultToNullIfBlank(proposalNumber)); proposal.setProposalStatusCode(defaultToNullIfBlank(status)); proposal.setAgencyNumber(defaultToNullIfBlank(agencyNumber)); @@ -338,7 +338,7 @@ private ProposalProjectDirector buildProposalProjectDirector(RassXMLAwardPiCoPiE public Award toAward() { Award award = new Award(); - award.setProposalAwardTypeCode(RassTestConstants.DEFAULT_PROPOSAL_AWARD_TYPE); + award.setAwardTypeCode(RassTestConstants.DEFAULT_PROPOSAL_AWARD_TYPE); award.setProposalNumber(defaultToNullIfBlank(proposalNumber)); award.setAwardStatusCode(defaultToNullIfBlank(status)); award.setAgencyNumber(defaultToNullIfBlank(agencyNumber)); diff --git a/src/test/java/edu/cornell/kfs/sys/businessobject/lookup/CuBatchFileLookupableHelperServiceImplTest.java b/src/test/java/edu/cornell/kfs/sys/businessobject/lookup/CuBatchFileLookupableHelperServiceImplTest.java index 9103aea2fa..55d8a92afe 100644 --- a/src/test/java/edu/cornell/kfs/sys/businessobject/lookup/CuBatchFileLookupableHelperServiceImplTest.java +++ b/src/test/java/edu/cornell/kfs/sys/businessobject/lookup/CuBatchFileLookupableHelperServiceImplTest.java @@ -10,6 +10,8 @@ import java.io.File; import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -24,7 +26,7 @@ public class CuBatchFileLookupableHelperServiceImplTest { public static final String FIXTURE_SUB2_PATH = "edu/cornell/kfs/sys/businessobject/lookup/fixture/sub2"; private LookupableHelperService cuLookupableHelperService; - private List rootDirectories; + private List rootDirectories; private List fileNames; private String userDir; private String fileSeparator; @@ -41,11 +43,11 @@ public void setUp() throws IOException { fileNames = setupFileNames(rootDirectories); } - private List setupFileNames(List rootDirectories) throws IOException { + private List setupFileNames(List rootDirectories) throws IOException { List fileNames = new ArrayList<>(); - for (File rootDirectory : rootDirectories) { - for (String fileName: FileUtils.getFileNames(rootDirectory, null, null, true)) { + for (Path rootDirectory : rootDirectories) { + for (String fileName: FileUtils.getFileNames(rootDirectory.toFile(), null, null, true)) { final int lastIndexOfFileSeparator = fileName.lastIndexOf(fileSeparator); if (lastIndexOfFileSeparator > 0) { @@ -108,21 +110,21 @@ private class TestableCuBatchFileLookupableHelperServiceImpl extends CuBatchFile public static final String SYS_PATH = "edu/cornell/kfs/sys"; private String[] selectedPaths; - protected List retrieveRootDirectories() { - List rootDirectories = new ArrayList<>(); + protected List retrieveRootDirectories() { + List rootDirectories = new ArrayList<>(); - rootDirectories.add(new File(filePrefix + FP_PATH)); - rootDirectories.add(new File(filePrefix + SYS_PATH)); + rootDirectories.add(Paths.get(filePrefix + FP_PATH).toAbsolutePath()); + rootDirectories.add(Paths.get(filePrefix + SYS_PATH).toAbsolutePath()); return rootDirectories; } - protected List getSelectedDirectories(String[] selectedPaths) { - List selectedDirectories = new ArrayList<>(); + protected List getSelectedDirectories(String[] selectedPaths) { + List selectedDirectories = new ArrayList<>(); if (ObjectUtils.isNotNull(getSelectedPaths())) { for (String selectedPath: getSelectedPaths()) { - selectedDirectories.add(new File(filePrefix + selectedPath)); + selectedDirectories.add(Paths.get(filePrefix + selectedPath).toAbsolutePath()); } } From a1056cdf08a6c0b87f46074ee1c0ab111bef4d71 Mon Sep 17 00:00:00 2001 From: Daniela Balmus Date: Fri, 6 Feb 2026 10:19:10 +0000 Subject: [PATCH 05/12] KFSPTS-36246 add changes to overlays --- .../web/struts/DisbursementVoucherAction.java | 67 +++++++++-------- .../impl/AccountBalanceDaoSqlHelper.java | 74 ++++++------------- .../struts/BalanceInquiryLookupAction.java | 2 +- .../web/ActionListFilterAction.java | 11 +-- .../kfs/kew/engine/BlanketApproveEngine.java | 2 +- .../type/DataDictionaryTypeServiceBase.java | 5 +- 6 files changed, 66 insertions(+), 95 deletions(-) diff --git a/src/main/java/org/kuali/kfs/fp/document/web/struts/DisbursementVoucherAction.java b/src/main/java/org/kuali/kfs/fp/document/web/struts/DisbursementVoucherAction.java index d3e0054759..a73f438464 100644 --- a/src/main/java/org/kuali/kfs/fp/document/web/struts/DisbursementVoucherAction.java +++ b/src/main/java/org/kuali/kfs/fp/document/web/struts/DisbursementVoucherAction.java @@ -120,13 +120,13 @@ protected void loadDocument(final KualiDocumentFormBase kualiDocumentFormBase) { dvDoc.getDvPayeeDetail().setDisbVchrPayeeEmployeeCode(false); } } - + if (dvDoc.getDocumentHeader().getWorkflowDocument().checkStatus(DocumentStatus.SAVED) || - dvDoc.getDocumentHeader().getWorkflowDocument().checkStatus(DocumentStatus.ENROUTE)) { - checkForDuplicatePayments(dvDoc); + dvDoc.getDocumentHeader().getWorkflowDocument().checkStatus(DocumentStatus.ENROUTE)) { + checkForDuplicatePayments(dvDoc); } } - + @Override public ActionForward save( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, @@ -193,7 +193,7 @@ public ActionForward execute( return dest; } - + private void populatePaymentMethodCodesRequiringAdditionalDvData(final DisbursementVoucherForm dvForm) { if (dvForm.getPaymentMethodCodesRequiringAdditionalData() != null) { return; @@ -236,7 +236,7 @@ public ActionForward updateBankBasedOnPaymentMethod( @Override public ActionForward approve( - final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, + final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, final HttpServletResponse response) throws Exception { final DisbursementVoucherForm dvForm = (DisbursementVoucherForm) form; SpringContext.getBean(DisbursementVoucherPayeeService.class).checkPayeeAddressForChanges((DisbursementVoucherDocument) dvForm.getDocument()); @@ -264,7 +264,7 @@ public ActionForward printDisbursementVoucherCoverSheet( final DisbursementVoucherDocument document = (DisbursementVoucherDocument) SpringContext.getBean( DocumentService.class).getByDocumentHeaderId( request.getParameter(KFSPropertyConstants.DOCUMENT_NUMBER)); - + // set document back into form to prevent "java.lang.IllegalArgumentException: documentId was null or blank" // error when checking permissions since we are bypassing form submit and just linking directly to the action dvForm.setDocument(document); @@ -279,13 +279,12 @@ public ActionForward printDisbursementVoucherCoverSheet( return null; } - /** * Calculates the travel per diem amount. */ public ActionForward calculateTravelPerDiem( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final DisbursementVoucherForm dvForm = (DisbursementVoucherForm) form; try { @@ -319,7 +318,7 @@ public ActionForward calculateTravelPerDiem( */ public ActionForward clearTravelPerDiem( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final DisbursementVoucherForm dvForm = (DisbursementVoucherForm) form; final DisbursementVoucherDocument dvDocument = (DisbursementVoucherDocument) dvForm.getDocument(); @@ -344,7 +343,7 @@ protected void clearTravelPerDiem(final DisbursementVoucherNonEmployeeTravel dvN */ public ActionForward calculateTravelMileageAmount( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final DisbursementVoucherForm dvForm = (DisbursementVoucherForm) form; final DisbursementVoucherDocument dvDocument = (DisbursementVoucherDocument) dvForm.getDocument(); @@ -378,7 +377,7 @@ public ActionForward calculateTravelMileageAmount( */ public ActionForward clearTravelMileageAmount( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final DisbursementVoucherForm dvForm = (DisbursementVoucherForm) form; final DisbursementVoucherDocument dvDocument = (DisbursementVoucherDocument) dvForm.getDocument(); @@ -403,7 +402,7 @@ protected void clearTravelMileageAmount(final DisbursementVoucherNonEmployeeTrav */ public ActionForward addNonEmployeeExpenseLine( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final DisbursementVoucherForm dvForm = (DisbursementVoucherForm) form; final DisbursementVoucherDocument dvDocument = (DisbursementVoucherDocument) dvForm.getDocument(); @@ -448,7 +447,7 @@ public ActionForward addNonEmployeeExpenseLine( */ public ActionForward addPrePaidNonEmployeeExpenseLine( final ActionMapping mapping, final ActionForm form, - final HttpServletRequest request, final HttpServletResponse response) throws Exception { + final HttpServletRequest request, final HttpServletResponse response) { final DisbursementVoucherForm dvForm = (DisbursementVoucherForm) form; final DisbursementVoucherDocument dvDocument = (DisbursementVoucherDocument) dvForm.getDocument(); @@ -492,7 +491,7 @@ public ActionForward addPrePaidNonEmployeeExpenseLine( */ public ActionForward deleteNonEmployeeExpenseLine( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final DisbursementVoucherForm dvForm = (DisbursementVoucherForm) form; final DisbursementVoucherDocument dvDocument = (DisbursementVoucherDocument) dvForm.getDocument(); @@ -507,7 +506,7 @@ public ActionForward deleteNonEmployeeExpenseLine( */ public ActionForward deletePrePaidEmployeeExpenseLine( final ActionMapping mapping, final ActionForm form, - final HttpServletRequest request, final HttpServletResponse response) throws Exception { + final HttpServletRequest request, final HttpServletResponse response) { final DisbursementVoucherForm dvForm = (DisbursementVoucherForm) form; final DisbursementVoucherDocument dvDocument = (DisbursementVoucherDocument) dvForm.getDocument(); @@ -522,7 +521,7 @@ public ActionForward deletePrePaidEmployeeExpenseLine( */ public ActionForward addPreConfRegistrantLine( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final DisbursementVoucherForm dvForm = (DisbursementVoucherForm) form; final DisbursementVoucherDocument dvDocument = (DisbursementVoucherDocument) dvForm.getDocument(); @@ -546,7 +545,7 @@ public ActionForward addPreConfRegistrantLine( */ public ActionForward deletePreConfRegistrantLine( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final DisbursementVoucherForm dvForm = (DisbursementVoucherForm) form; final DisbursementVoucherDocument dvDocument = (DisbursementVoucherDocument) dvForm.getDocument(); @@ -561,7 +560,7 @@ public ActionForward deletePreConfRegistrantLine( */ public ActionForward generateNonresidentTaxLines( final ActionMapping mapping, final ActionForm form, - final HttpServletRequest request, final HttpServletResponse response) throws Exception { + final HttpServletRequest request, final HttpServletResponse response) { final DisbursementVoucherForm dvForm = (DisbursementVoucherForm) form; final DisbursementVoucherDocument document = (DisbursementVoucherDocument) dvForm.getDocument(); @@ -580,7 +579,7 @@ public ActionForward generateNonresidentTaxLines( */ public ActionForward clearNonresidentTaxLines( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final DisbursementVoucherForm dvForm = (DisbursementVoucherForm) form; final DisbursementVoucherDocument document = (DisbursementVoucherDocument) dvForm.getDocument(); @@ -597,7 +596,7 @@ public ActionForward clearNonresidentTaxLines( */ public ActionForward clearNonresidentTaxInfo( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final DisbursementVoucherForm dvForm = (DisbursementVoucherForm) form; final DisbursementVoucherDocument document = (DisbursementVoucherDocument) dvForm.getDocument(); @@ -711,7 +710,7 @@ protected ActionForward refreshAfterPayeeSelection( (List) getBusinessObjectService().findMatching(CustomerAddress.class, addressSearch ); - + if (customerAddresses != null && !customerAddresses.isEmpty()) { if (customerAddresses.size() > 1) { dvForm.setHasMultipleAddresses(true); @@ -790,10 +789,10 @@ protected ActionForward renderVendorAddressSelection( final String conversionPattern = "{0}" + KFSConstants.FIELD_CONVERSION_PAIR_SEPERATOR + "{0}"; final String filedConversion = MessageFormat.format(conversionPattern, KFSPropertyConstants.VENDOR_ADDRESS_GENERATED_ID) + - KFSConstants.FIELD_CONVERSIONS_SEPERATOR + - MessageFormat.format(conversionPattern, KFSPropertyConstants.VENDOR_HEADER_GENERATED_ID) + - KFSConstants.FIELD_CONVERSIONS_SEPERATOR + - MessageFormat.format(conversionPattern, KFSPropertyConstants.VENDOR_DETAIL_ASSIGNED_ID); + KFSConstants.FIELD_CONVERSIONS_SEPERATOR + + MessageFormat.format(conversionPattern, KFSPropertyConstants.VENDOR_HEADER_GENERATED_ID) + + KFSConstants.FIELD_CONVERSIONS_SEPERATOR + + MessageFormat.format(conversionPattern, KFSPropertyConstants.VENDOR_DETAIL_ASSIGNED_ID); props.put(KRADConstants.CONVERSION_FIELDS_PARAMETER, filedConversion); props.put(KFSPropertyConstants.VENDOR_HEADER_GENERATED_ID, dvForm.getVendorHeaderGeneratedIdentifier()); @@ -875,7 +874,7 @@ protected ActionForward renderCustomerAddressSelection( final String conversionPattern = "{0}" + KFSConstants.FIELD_CONVERSION_PAIR_SEPERATOR + "{0}"; final String filedConversion = MessageFormat.format(conversionPattern, KFSPropertyConstants.CUSTOMER_NUMBER) + - KFSConstants.FIELD_CONVERSIONS_SEPERATOR + MessageFormat.format(conversionPattern, + KFSConstants.FIELD_CONVERSIONS_SEPERATOR + MessageFormat.format(conversionPattern, KFSPropertyConstants.CUSTOMER_ADDRESS_IDENTIFIER); props.put(KRADConstants.CONVERSION_FIELDS_PARAMETER, filedConversion); @@ -936,7 +935,7 @@ protected void addPaymentCodeWarningMessage(final DisbursementVoucherForm dvForm } final String reasonCodeProperty = KFSPropertyConstants.DOCUMENT + "." + KFSPropertyConstants.DV_PAYEE_DETAIL + "." + - KFSPropertyConstants.DISB_VCHR_PAYMENT_REASON_CODE; + KFSPropertyConstants.DISB_VCHR_PAYMENT_REASON_CODE; GlobalVariables.getMessageMap().removeAllWarningMessagesForProperty(reasonCodeProperty); // add warning message and reset tab state as open if any @@ -946,14 +945,14 @@ protected void addPaymentCodeWarningMessage(final DisbursementVoucherForm dvForm GlobalVariables.getMessageMap().putWarning(reasonCodeProperty, tab.messageKey); GlobalVariables.getMessageMap().putWarning(tab.getDocumentPropertyKey(), tab.messageKey); } - } + } /** * Extracts the DV as immediate payment upon user's request after it routes to FINAL. */ public ActionForward extractNow( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final DisbursementVoucherForm dvForm = (DisbursementVoucherForm) form; final DisbursementVoucherDocument dvDocument = (DisbursementVoucherDocument) dvForm.getDocument(); @@ -968,10 +967,10 @@ protected DisbursementVoucherPayeeService getDisbursementVoucherPayeeService() { if (disbursementVoucherPayeeService == null) { disbursementVoucherPayeeService = SpringContext.getBean(DisbursementVoucherPayeeService.class); } - + return disbursementVoucherPayeeService; } - + protected DisbursementVoucherValidationService getDisbursementVoucherValidationService() { if (disbursementVoucherValidationService == null) { disbursementVoucherValidationService = SpringContext.getBean(DisbursementVoucherValidationService.class); @@ -979,7 +978,7 @@ protected DisbursementVoucherValidationService getDisbursementVoucherValidationS return disbursementVoucherValidationService; } - + private BankService getBankService() { if (bankService == null) { bankService = SpringContext.getBean(BankService.class); @@ -993,4 +992,4 @@ public PaymentSourceHelperService getPaymentSourceHelperService() { } return paymentSourceHelperService; } -} \ No newline at end of file +} diff --git a/src/main/java/org/kuali/kfs/gl/dataaccess/impl/AccountBalanceDaoSqlHelper.java b/src/main/java/org/kuali/kfs/gl/dataaccess/impl/AccountBalanceDaoSqlHelper.java index 73a75889ea..859cb89ad5 100644 --- a/src/main/java/org/kuali/kfs/gl/dataaccess/impl/AccountBalanceDaoSqlHelper.java +++ b/src/main/java/org/kuali/kfs/gl/dataaccess/impl/AccountBalanceDaoSqlHelper.java @@ -26,6 +26,7 @@ import org.kuali.kfs.gl.Constant; import org.kuali.kfs.gl.GeneralLedgerConstants; import org.kuali.kfs.gl.businessobject.AccountBalance; +import org.kuali.kfs.gl.dataaccess.GeneralLedgerSqlHelper; import org.kuali.kfs.sys.KFSConstants; import org.kuali.kfs.sys.KFSPropertyConstants; import org.kuali.kfs.sys.businessobject.SystemOptions; @@ -33,12 +34,11 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -47,7 +47,7 @@ */ // Exposing for overriding in UConn // CU customization: adjust code to be Oracle compliant -public class AccountBalanceDaoSqlHelper { +public class AccountBalanceDaoSqlHelper extends GeneralLedgerSqlHelper { private static final Logger LOG = LogManager.getLogger(); private static final String ORGANIZATION_CODE_CRITERIA = "account.organizationCode"; @@ -82,8 +82,6 @@ public class AccountBalanceDaoSqlHelper { ); // Exposing for accessing in UConn - protected final Map parameters = new HashMap<>(); - protected final Map fieldValues; protected final boolean isConsolidated; protected final String pendingEntryOption; protected final boolean excludeTransfers; @@ -101,7 +99,7 @@ public AccountBalanceDaoSqlHelper( final ParameterService parameterService, final SystemOptions systemOptions ) { - this.fieldValues = fieldValues; + super(fieldValues); this.isConsolidated = isConsolidated; // Need to default here due to overlays using old UI do not pass this value if unchanged pendingEntryOption = fieldValues.getOrDefault(Constant.PENDING_ENTRY_OPTION, Constant.NO_PENDING_ENTRY); @@ -128,9 +126,8 @@ public AccountBalanceDaoSqlHelper( } // Exposing for overriding in UConn - protected AccountBalance mapResultSetToAccountBalance( - final ResultSet rs - ) throws SQLException { + @Override + protected AccountBalance mapResultSetToObject(final Supplier supplier, final ResultSet rs) throws SQLException { final AccountBalance accountBalance = new AccountBalance(); accountBalance.setAccountNumber(rs.getString("ACCOUNT_NBR")); accountBalance.setChartOfAccountsCode(rs.getString("FIN_COA_CD")); @@ -151,33 +148,9 @@ protected AccountBalance mapResultSetToAccountBalance( return accountBalance; } - private String addSortAndLimitSql( - final String baseSql - ) { - String orderedAndLimited = baseSql; - final int limit = Integer.parseInt(fieldValues.getOrDefault(KFSConstants.Search.LIMIT, "100")); - final int skip = Integer.parseInt(fieldValues.getOrDefault(KFSConstants.Search.SKIP, "0")); - final String sort = fieldValues.get(KFSConstants.Search.SORT); - if (StringUtils.isNotBlank(sort)) { - final boolean sortDescending = sort.startsWith("-"); - final String sortField = sort.substring(sortDescending ? 1 : 0); - final Map sortMap = isConsolidated ? CONSOLIDATED_SORT_MAP : UNCONSOLIDATED_SORT_MAP; - if (sortMap.containsKey(sortField)) { - orderedAndLimited += " ORDER BY " + sortMap.get(sortField) + (sortDescending ? " DESC " : " ASC "); - } - } - // CU customization: replace MySql specific syntax 'LIMIT x OFFSET y' with Oracle compliant 'OFFSET y ROWS FETCH NEXT x ROWS ONLY' - orderedAndLimited += " OFFSET " + skip + " ROWS FETCH NEXT " + limit + " ROWS ONLY"; - return orderedAndLimited; - } - // Exposing for override in Overlays protected String addSearchCriteria() { - String searchCriteria = CRITERIA_MAP.entrySet() - .stream() - .map(entry -> addCondition(parameters, fieldValues, entry.getKey(), entry.getValue())) - .filter(StringUtils::isNotBlank) - .collect(Collectors.joining(" AND ")); + String searchCriteria = addSearchCriteria(CRITERIA_MAP); if (excludeTransfers) { final Collection transferExclusions = parameterService.getParameterValuesAsString(AccountBalance.class, @@ -206,14 +179,17 @@ protected String addSearchCriteria() { } // Exposing for accessing in Overlays + @Override protected String addCondition( final Map parameters, - final Map fieldValues, final String fieldValueKey, final String sqlField ) { + if (!StringUtils.equals(fieldValueKey, KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR)) { + return super.addCondition(parameters, fieldValueKey, sqlField); + } String condition = ""; - String fieldValue = fieldValues.get(fieldValueKey); + final String fieldValue = fieldValues.get(fieldValueKey); if (StringUtils.isNotBlank(fieldValue)) { if (StringUtils.equals(fieldValueKey, KFSPropertyConstants.UNIVERSITY_FISCAL_YEAR) && !excludePriorYearBalances) { @@ -224,19 +200,14 @@ protected String addCondition( condition = sqlField + " IN (:" + fieldValueKey + ") "; parameters.put(fieldValueKey, List.of(fieldValue, Integer.parseInt(fieldValue) - 1)); } else { - fieldValue = fieldValue.replace("*", "%"); - parameters.put(fieldValueKey, fieldValue); - if (fieldValue.contains("%")) { - condition = sqlField + " LIKE :" + fieldValueKey + " "; - } else { - condition = sqlField + " = :" + fieldValueKey + " "; - } + return super.addCondition(parameters, fieldValueKey, sqlField); } } return condition; } - String buildCountSql() { + @Override + protected String buildCountSql() { final String countSql; if (StringUtils.equals(Constant.NO_PENDING_ENTRY, pendingEntryOption)) { countSql = wrap(buildFinalSql()); @@ -247,15 +218,16 @@ String buildCountSql() { return "SELECT COUNT(*) FROM (" + countSql + ") BALANCE_COUNT"; } - Map getParameters() { - return Collections.unmodifiableMap(parameters); - } - - String buildSql() { + @Override + protected String buildSql() { if (!StringUtils.equals(Constant.NO_PENDING_ENTRY, pendingEntryOption)) { - return addSortAndLimitSql(buildPendingEntrySql()); + return addSortAndLimitSql(buildPendingEntrySql(), + isConsolidated ? CONSOLIDATED_SORT_MAP : UNCONSOLIDATED_SORT_MAP + ); } - return addSortAndLimitSql(wrap(buildFinalSql())); + return addSortAndLimitSql(wrap(buildFinalSql()), + isConsolidated ? CONSOLIDATED_SORT_MAP : UNCONSOLIDATED_SORT_MAP + ); } private String buildPendingEntrySql() { diff --git a/src/main/java/org/kuali/kfs/gl/web/struts/BalanceInquiryLookupAction.java b/src/main/java/org/kuali/kfs/gl/web/struts/BalanceInquiryLookupAction.java index ea82f5c3d3..1e62ebc5d3 100644 --- a/src/main/java/org/kuali/kfs/gl/web/struts/BalanceInquiryLookupAction.java +++ b/src/main/java/org/kuali/kfs/gl/web/struts/BalanceInquiryLookupAction.java @@ -116,7 +116,7 @@ private String[] getTotalTitles() { @Override public ActionForward search( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final BalanceInquiryLookupForm lookupForm = (BalanceInquiryLookupForm) form; final Lookupable lookupable = lookupForm.getLookupable(); diff --git a/src/main/java/org/kuali/kfs/kew/actionlist/web/ActionListFilterAction.java b/src/main/java/org/kuali/kfs/kew/actionlist/web/ActionListFilterAction.java index 2409cb5c55..1bfba7b94d 100644 --- a/src/main/java/org/kuali/kfs/kew/actionlist/web/ActionListFilterAction.java +++ b/src/main/java/org/kuali/kfs/kew/actionlist/web/ActionListFilterAction.java @@ -69,8 +69,7 @@ public ActionForward execute( public ActionForward start( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws - Exception { + final HttpServletResponse response) { final ActionListFilterForm filterForm = (ActionListFilterForm) form; final UserSession uSession = getUserSession(); final ActionListFilter filter = @@ -90,8 +89,7 @@ public ActionForward start( public ActionForward filter( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws - Exception { + final HttpServletResponse response) { final ActionListFilterForm filterForm = (ActionListFilterForm) form; //validate the filter through the actionitem/actionlist service (I'm thinking actionlistservice) final UserSession uSession = getUserSession(); @@ -118,8 +116,7 @@ public ActionForward filter( */ public ActionForward clear( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws - Exception { + final HttpServletResponse response) { final ActionListFilterForm filterForm = (ActionListFilterForm) form; filterForm.setFilter(new ActionListFilter()); filterForm.setCreateDateFrom(""); @@ -134,7 +131,7 @@ public ActionForward clear( return mapping.findForward("viewFilter"); } - public void initForm(final HttpServletRequest request, final ActionForm form) throws Exception { + public void initForm(final HttpServletRequest request, final ActionForm form) { final ActionListFilterForm filterForm = (ActionListFilterForm) form; filterForm.setUserWorkgroups(getUserWorkgroupsDropDownList(getUserSession().getPrincipalId())); final PreferencesService prefSrv = KewApiServiceLocator.getPreferencesService(); diff --git a/src/main/java/org/kuali/kfs/kew/engine/BlanketApproveEngine.java b/src/main/java/org/kuali/kfs/kew/engine/BlanketApproveEngine.java index 57cb5b7953..7eebb158e3 100644 --- a/src/main/java/org/kuali/kfs/kew/engine/BlanketApproveEngine.java +++ b/src/main/java/org/kuali/kfs/kew/engine/BlanketApproveEngine.java @@ -219,7 +219,7 @@ private List determineNodeInstancesToProcess( return nodeInstancesToProcess; } - private boolean isNodeNameInPath(final Set nodeNames, final RouteNodeInstance nodeInstance) throws Exception { + private boolean isNodeNameInPath(final Set nodeNames, final RouteNodeInstance nodeInstance) { boolean isInPath = false; for (final Object nodeName1 : nodeNames) { final String nodeName = (String) nodeName1; diff --git a/src/main/java/org/kuali/kfs/kns/kim/type/DataDictionaryTypeServiceBase.java b/src/main/java/org/kuali/kfs/kns/kim/type/DataDictionaryTypeServiceBase.java index 2f0c863efa..d92b3c65e8 100644 --- a/src/main/java/org/kuali/kfs/kns/kim/type/DataDictionaryTypeServiceBase.java +++ b/src/main/java/org/kuali/kfs/kns/kim/type/DataDictionaryTypeServiceBase.java @@ -49,6 +49,7 @@ import org.kuali.kfs.krad.datadictionary.AttributeDefinition; import org.kuali.kfs.krad.datadictionary.PrimitiveAttributeDefinition; import org.kuali.kfs.krad.datadictionary.RelationshipDefinition; +import org.kuali.kfs.krad.datadictionary.validation.ValidationUtils; import org.kuali.kfs.krad.service.BusinessObjectService; import org.kuali.kfs.krad.service.KRADServiceLocator; import org.kuali.kfs.krad.util.ErrorMessage; @@ -349,7 +350,9 @@ protected List validateDataDictionaryAttribute( // not a super-awesome fallback strategy, but... attributeValue = value; } - propertyDescriptor.getWriteMethod().invoke(componentObject, attributeValue); + if (!ValidationUtils.isNullOrEmpty(attributeValue)) { + propertyDescriptor.getWriteMethod().invoke(componentObject, attributeValue); + } return validateDataDictionaryAttribute(attr.getKimTypeId(), attr.getKimAttribute().getComponentName(), componentObject, propertyDescriptor); } From 01ad4c85965286e033587a4270ac83481593ff69 Mon Sep 17 00:00:00 2001 From: Daniela Balmus Date: Fri, 6 Feb 2026 13:58:01 +0000 Subject: [PATCH 06/12] KFSPTS-36246 add changes to overlays --- .../web/struts/IWantDocumentAction.java | 2 +- .../action/KualiDocumentActionBase.java | 40 ++++++++----------- .../web/struts/PaymentApplicationAction.java | 18 ++++----- ...rantsInvoiceCreateDocumentServiceImpl.java | 15 +++---- .../struts/FringeBenefitInquiryAction.java | 2 +- .../web/struts/PurchasingActionBase.java | 22 ++++++---- 6 files changed, 49 insertions(+), 50 deletions(-) diff --git a/src/main/java/edu/cornell/kfs/module/purap/document/web/struts/IWantDocumentAction.java b/src/main/java/edu/cornell/kfs/module/purap/document/web/struts/IWantDocumentAction.java index a2c35500e7..e2e30524d3 100644 --- a/src/main/java/edu/cornell/kfs/module/purap/document/web/struts/IWantDocumentAction.java +++ b/src/main/java/edu/cornell/kfs/module/purap/document/web/struts/IWantDocumentAction.java @@ -928,7 +928,7 @@ private boolean documentRoutingSuccessMessageIsPresent() { @Override public ActionForward promptBeforeValidation( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response, final String methodToCall) throws Exception { + final HttpServletResponse response, final String methodToCall) { final IWantDocumentForm iWantDocForm = (IWantDocumentForm) form; if (!StringUtils.equalsIgnoreCase(iWantDocForm.getStep(), CUPurapConstants.IWantDocumentSteps.REGULAR)) { return null; diff --git a/src/main/java/org/kuali/kfs/kns/web/struts/action/KualiDocumentActionBase.java b/src/main/java/org/kuali/kfs/kns/web/struts/action/KualiDocumentActionBase.java index a900623d24..6ec12d4f9e 100644 --- a/src/main/java/org/kuali/kfs/kns/web/struts/action/KualiDocumentActionBase.java +++ b/src/main/java/org/kuali/kfs/kns/web/struts/action/KualiDocumentActionBase.java @@ -450,7 +450,7 @@ protected void createDocument(final KualiDocumentFormBase kualiDocumentFormBase) */ public ActionForward insertAdHocRoutePerson( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; final Document document = kualiDocumentFormBase.getDocument(); // check authorization for adding ad hoc route person @@ -485,11 +485,10 @@ public ActionForward insertAdHocRoutePerson( * @param request * @param response * @return ActionForward - * @throws Exception */ public ActionForward deleteAdHocRoutePerson( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; kualiDocumentFormBase.getAdHocRoutePersons().remove(getLineToDelete(request)); return mapping.findForward(KFSConstants.MAPPING_BASIC); @@ -504,11 +503,10 @@ public ActionForward deleteAdHocRoutePerson( * @param request * @param response * @return ActionForward - * @throws Exception */ public ActionForward insertAdHocRouteWorkgroup( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; final Document document = kualiDocumentFormBase.getDocument(); @@ -536,11 +534,10 @@ public ActionForward insertAdHocRouteWorkgroup( * @param request * @param response * @return ActionForward - * @throws Exception */ public ActionForward deleteAdHocRouteWorkgroup( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; kualiDocumentFormBase.getAdHocRouteWorkgroups().remove(getLineToDelete(request)); @@ -640,13 +637,11 @@ public ActionForward save( * @param context additional context that needs to be passed back with the question response * @return ActionForward which contains the question forward, or basic forward if user select no to prompt, * otherwise will return null to indicate processing should continue - * @throws Exception */ protected ActionForward checkAndWarnAboutSensitiveData( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, final HttpServletResponse response, final String fieldName, final String fieldValue, - final String caller, final String context) - throws Exception { + final String caller, final String context) { final KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; final Document document = kualiDocumentFormBase.getDocument(); @@ -852,9 +847,8 @@ public ActionForward disapprove( * @param request * @param reason * - * @throws Exception */ - protected void doDisapprove(final KualiDocumentFormBase form, final HttpServletRequest request, final String reason) throws Exception { + protected void doDisapprove(final KualiDocumentFormBase form, final HttpServletRequest request, final String reason) { doProcessingAfterPost(form, request); getDocumentService().disapproveDocument(form.getDocument(), reason); KNSGlobalVariables.getMessageList().add(MESSAGE_ROUTE_DISAPPROVED); @@ -1206,11 +1200,10 @@ protected void refreshAdHocRoutingWorkgroupLookups(final HttpServletRequest requ * @param request * @param response * @return ActionForward - * @throws Exception */ public ActionForward cancelBOAttachment( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final KualiDocumentFormBase documentForm = (KualiDocumentFormBase) form; // blank current attachmentFile @@ -1466,6 +1459,11 @@ public ActionForward deleteBONote( final Note note = document.getNote(noteIndex); deleteNoteFromDocument(document, note); + // if the document isn't approved, save it so the deleted note/attachment stays deleted + final WorkflowDocument workflowDocument = document.getDocumentHeader().getWorkflowDocument(); + if (!workflowDocument.isApproved()) { + getDocumentService().saveDocument(document); + } return mapping.findForward(KFSConstants.MAPPING_BASIC); } @@ -1511,7 +1509,7 @@ public String determineNoteWorkflowNotificationAction( public ActionForward sendNoteWorkflowNotification( final ActionMapping mapping, final ActionForm form, - final HttpServletRequest request, final HttpServletResponse response) throws Exception { + final HttpServletRequest request, final HttpServletResponse response) { final KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; final Document document = kualiDocumentFormBase.getDocument(); @@ -1584,11 +1582,10 @@ private String createOjbOptimisticLockExceptionLogMsg(final OptimisticLockExcept * @param request * @param response * @return - * @throws Exception */ public ActionForward promptBeforeValidation( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { return promptBeforeValidation(mapping, form, request, response, "route"); } @@ -1602,11 +1599,10 @@ public ActionForward promptBeforeValidation( * @param response * @param methodToCall * @return - * @throws Exception */ public ActionForward promptBeforeValidation( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response, final String methodToCall) throws Exception { + final HttpServletResponse response, final String methodToCall) { final KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; /* callback to any pre rules check class */ @@ -2056,11 +2052,10 @@ public WorkflowDocumentActionsService getWorkflowDocumentActionsService() { * @param request * @param response * @return - * @throws Exception */ public ActionForward complete( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final KualiDocumentFormBase kualiDocumentFormBase = (KualiDocumentFormBase) form; doProcessingAfterPost(kualiDocumentFormBase, request); @@ -2143,11 +2138,10 @@ public ReasonPrompt( * @param response http response * @return Response object representing *either*: 1) an ActionForward due to error or abort 2) a reason and * button clicked - * @throws Exception */ public Response ask( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final String question = request.getParameter(KRADConstants.QUESTION_INST_ATTRIBUTE_NAME); String reason = request.getParameter(KRADConstants.QUESTION_REASON_ATTRIBUTE_NAME); diff --git a/src/main/java/org/kuali/kfs/module/ar/document/web/struts/PaymentApplicationAction.java b/src/main/java/org/kuali/kfs/module/ar/document/web/struts/PaymentApplicationAction.java index e06f60b7e6..5062eb40fa 100644 --- a/src/main/java/org/kuali/kfs/module/ar/document/web/struts/PaymentApplicationAction.java +++ b/src/main/java/org/kuali/kfs/module/ar/document/web/struts/PaymentApplicationAction.java @@ -158,7 +158,7 @@ public ActionForward save( public ActionForward deleteNonArLine( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PaymentApplicationForm paymentApplicationForm = (PaymentApplicationForm) form; final PaymentApplicationDocument paymentApplicationDocument = paymentApplicationForm.getPaymentApplicationDocument(); @@ -204,14 +204,14 @@ protected InvoicePaidApplied generateAndValidateNewPaidApplied( public ActionForward applyAllAmounts( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { doApplicationOfFunds((PaymentApplicationForm) form); return mapping.findForward(KFSConstants.MAPPING_BASIC); } public ActionForward clearUnapplied( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PaymentApplicationForm payAppForm = (PaymentApplicationForm) form; final PaymentApplicationDocument payAppDoc = payAppForm.getPaymentApplicationDocument(); final NonAppliedHolding nonAppliedHolding = payAppDoc.getNonAppliedHolding(); @@ -592,11 +592,10 @@ protected NonAppliedHolding applyUnapplied(final PaymentApplicationForm payAppFo * @param request * @param response * @return - * @throws Exception */ public ActionForward loadInvoices( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PaymentApplicationForm pform = (PaymentApplicationForm) form; loadInvoices(pform, pform.getEnteredInvoiceDocumentNumber()); return mapping.findForward(KFSConstants.MAPPING_BASIC); @@ -814,11 +813,10 @@ protected List sortInvoiceApplications( * @param request * @param response * @return - * @throws Exception */ public ActionForward goToInvoice( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PaymentApplicationForm payAppForm = (PaymentApplicationForm) form; loadInvoices(payAppForm, payAppForm.getSelectedInvoiceDocumentNumber()); if (!payAppForm.getPaymentApplicationDocument().isFinal()) { @@ -835,11 +833,10 @@ public ActionForward goToInvoice( * @param request * @param response * @return - * @throws Exception */ public ActionForward goToNextInvoice( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PaymentApplicationForm payAppForm = (PaymentApplicationForm) form; loadInvoices(payAppForm, payAppForm.getNextInvoiceDocumentNumber()); if (!payAppForm.getPaymentApplicationDocument().isFinal()) { @@ -856,11 +853,10 @@ public ActionForward goToNextInvoice( * @param request * @param response * @return - * @throws Exception */ public ActionForward goToPreviousInvoice( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final PaymentApplicationForm payAppForm = (PaymentApplicationForm) form; loadInvoices(payAppForm, payAppForm.getPreviousInvoiceDocumentNumber()); if (!payAppForm.getPaymentApplicationDocument().isFinal()) { diff --git a/src/main/java/org/kuali/kfs/module/ar/service/impl/ContractsGrantsInvoiceCreateDocumentServiceImpl.java b/src/main/java/org/kuali/kfs/module/ar/service/impl/ContractsGrantsInvoiceCreateDocumentServiceImpl.java index 8eadc91b55..42ce9cf0b7 100644 --- a/src/main/java/org/kuali/kfs/module/ar/service/impl/ContractsGrantsInvoiceCreateDocumentServiceImpl.java +++ b/src/main/java/org/kuali/kfs/module/ar/service/impl/ContractsGrantsInvoiceCreateDocumentServiceImpl.java @@ -1317,6 +1317,9 @@ protected BigDecimal calculatePercentageByInvoiceDetailAccountObjectCodes( final List invoiceDetailAccountObjectCodes, final KualiDecimal total) { final KualiDecimal cumulativeExpenditureTotal = sumInvoiceDetailAccountObjectCodes( invoiceDetailAccountObjectCodes).getCumulativeExpenditures(); + if (total.isZero()) { + return BigDecimal.ZERO; + } return cumulativeExpenditureTotal.bigDecimalValue().divide(total.bigDecimalValue(), 10, RoundingMode.HALF_UP); } @@ -1804,8 +1807,8 @@ && getContractsGrantsBillingAwardVerificationService() */ award.setCreationProcessType(creationProcessType); if (verifyBillingFrequencyService.validateBillingFrequency(award, checkGracePeriod) - || ArConstants.BillingFrequencyValues.isMilestone(award) - || ArConstants.BillingFrequencyValues.isPredeterminedBilling(award)) { + || ArConstants.BillingFrequencyValues.isMilestone(award) + || ArConstants.BillingFrequencyValues.isPredeterminedBilling(award)) { validateAward(errorList, award, creationProcessType); } else { errorList.add(configurationService.getPropertyValueAsString( @@ -2119,7 +2122,7 @@ protected List retrieveContractsGrantsInvoiceDocumentsToRoute( protected void writeErrorEntryByAward( final Award award, final List validationCategory, - final PrintStream printStream) throws IOException { + final PrintStream printStream) { // %15s %18s %20s %19s %15s %18s %23s %18s if (ObjectUtils.isNotNull(award)) { KualiDecimal cumulativeExpenses = KualiDecimal.ZERO; @@ -2186,8 +2189,7 @@ protected void writeErrorEntryByAward( protected void writeToReport( final String proposalNumber, final String accountNumber, final String awardBeginningDate, - final String awardEndingDate, final String awardTotalAmount, final String cumulativeExpenses, final PrintStream printStream) - throws IOException { + final String awardEndingDate, final String awardTotalAmount, final String cumulativeExpenses, final PrintStream printStream) { printStream.printf("%15s", proposalNumber); printStream.printf("%18s", accountNumber); printStream.printf("%20s", awardBeginningDate); @@ -2199,9 +2201,8 @@ protected void writeToReport( /** * @param printStream - * @throws IOException */ - protected void writeReportHeader(final PrintStream printStream) throws IOException { + protected void writeReportHeader(final PrintStream printStream) { printStream.printf("%15s%18s%20s%19s%15s%23s\r\n", "Proposal Number", "Account Number", "Award Start Date", "Award Stop Date", "Award Total", "Cumulative Expenses"); printStream.printf("%23s", "Validation Category"); diff --git a/src/main/java/org/kuali/kfs/module/ld/document/web/struts/FringeBenefitInquiryAction.java b/src/main/java/org/kuali/kfs/module/ld/document/web/struts/FringeBenefitInquiryAction.java index c4452113ae..9343a34f3b 100644 --- a/src/main/java/org/kuali/kfs/module/ld/document/web/struts/FringeBenefitInquiryAction.java +++ b/src/main/java/org/kuali/kfs/module/ld/document/web/struts/FringeBenefitInquiryAction.java @@ -46,7 +46,7 @@ public class FringeBenefitInquiryAction extends KualiAction { public ActionForward calculateFringeBenefit( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final FringeBenefitInquiryForm accountingLineForm = (FringeBenefitInquiryForm) form; final Integer payrollFiscalYear = Integer.valueOf(accountingLineForm.getPayrollEndDateFiscalYear()); diff --git a/src/main/java/org/kuali/kfs/module/purap/document/web/struts/PurchasingActionBase.java b/src/main/java/org/kuali/kfs/module/purap/document/web/struts/PurchasingActionBase.java index 0ed074fae3..90f399b8ac 100644 --- a/src/main/java/org/kuali/kfs/module/purap/document/web/struts/PurchasingActionBase.java +++ b/src/main/java/org/kuali/kfs/module/purap/document/web/struts/PurchasingActionBase.java @@ -468,7 +468,7 @@ public ActionForward useOtherDeliveryBuilding( public ActionForward useOffCampusAssetLocationBuildingByDocument( final ActionMapping mapping, final ActionForm form, - final HttpServletRequest request, final HttpServletResponse response) throws Exception { + final HttpServletRequest request, final HttpServletResponse response) { final PurchasingFormBase baseForm = (PurchasingFormBase) form; final PurchasingDocument document = (PurchasingDocument) baseForm.getDocument(); @@ -1666,8 +1666,10 @@ public ActionForward route( * the user clicks on the approve button. */ @Override - public ActionForward approve(final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, final HttpServletResponse response) throws Exception { - final PurchasingFormBase purchasingForm = (PurchasingFormBase) form; + public ActionForward approve( + final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, + final HttpServletResponse response) throws Exception { + final PurchasingFormBase purchasingForm = (PurchasingFormBase) form; final PurchasingDocument purDoc = (PurchasingDocument) purchasingForm.getDocument(); if (isAttachmentSizeExceedSqLimit(form, "approve") || isReasonToChangeRequired(form)) { return mapping.findForward(KFSConstants.MAPPING_BASIC); @@ -1692,7 +1694,9 @@ public ActionForward approve(final ActionMapping mapping, final ActionForm form, } @Override - public ActionForward blanketApprove(final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, final HttpServletResponse response) throws Exception { + public ActionForward blanketApprove( + final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, + final HttpServletResponse response) throws Exception { final PurchasingFormBase purchasingForm = (PurchasingFormBase) form; final PurchasingDocument purDoc = (PurchasingDocument) purchasingForm.getDocument(); if (isAttachmentSizeExceedSqLimit(form, "blanket approve") || isReasonToChangeRequired(form)) { @@ -1974,14 +1978,18 @@ protected boolean requiresCalculate(final PurchasingFormBase purForm) { return requiresCalculate; } - public ActionForward populateBuilding(final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, final HttpServletResponse response) throws Exception { + public ActionForward populateBuilding( + final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, + final HttpServletResponse response) { final PurchasingFormBase purForm = (PurchasingFormBase) form; final PurchasingDocumentBase document = (PurchasingDocumentBase) purForm.getDocument(); updateAssetBuildingLocations(purForm, request, document); return mapping.findForward(KFSConstants.MAPPING_BASIC); } - - public ActionForward populateDeliveryBuilding(final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, final HttpServletResponse response) throws Exception { + + public ActionForward populateDeliveryBuilding( + final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, + final HttpServletResponse response) { final PurchasingFormBase purForm = (PurchasingFormBase) form; final PurchasingDocumentBase document = (PurchasingDocumentBase) purForm.getDocument(); updateDeliveryBuilding(request, document); From 22c4259f23d579d3ca3bd3c0205e296d10a4b340 Mon Sep 17 00:00:00 2001 From: Daniela Balmus Date: Fri, 6 Feb 2026 14:33:10 +0000 Subject: [PATCH 07/12] KFSPTS-36246 add changes to overlays --- .../kfs/pdp/web/struts/CuFormatAction.java | 2 +- .../kuali/kfs/module/purap/PurapConstants.java | 1 + .../service/impl/Iso20022FormatExtractor.java | 1 - .../kuali/kfs/pdp/businessobject/ACHPayee.java | 1 - .../service/ACHPayeeSearchService.java | 3 ++- .../kuali/kfs/pdp/web/struts/FormatAction.java | 15 +++++---------- .../org/kuali/kfs/sys/context/KFSConfigurer.java | 6 +++--- 7 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/main/java/edu/cornell/kfs/pdp/web/struts/CuFormatAction.java b/src/main/java/edu/cornell/kfs/pdp/web/struts/CuFormatAction.java index cf60a7670f..c96a0fccb6 100644 --- a/src/main/java/edu/cornell/kfs/pdp/web/struts/CuFormatAction.java +++ b/src/main/java/edu/cornell/kfs/pdp/web/struts/CuFormatAction.java @@ -36,7 +36,7 @@ public CuFormatAction() { @Override public ActionForward start( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final CuFormatForm formatForm = (CuFormatForm) form; final Person kualiUser = GlobalVariables.getUserSession().getPerson(); diff --git a/src/main/java/org/kuali/kfs/module/purap/PurapConstants.java b/src/main/java/org/kuali/kfs/module/purap/PurapConstants.java index 927bf5799c..7b53d67fc9 100644 --- a/src/main/java/org/kuali/kfs/module/purap/PurapConstants.java +++ b/src/main/java/org/kuali/kfs/module/purap/PurapConstants.java @@ -86,6 +86,7 @@ public final class PurapConstants { public static final String IMAGE_FILES_INDICATOR = "IMAGE_FILES_IND"; public static final String IMAGE_TEMP_PATH = "PDF_IMAGE_TEMP_PATH"; public static final String LOGO_FILE_EXTENSION = "LOGO_FILE_EXTENSION"; + public static final String LOGO_FILE_IND = "LOGO_FILE_IND"; public static final String LOGO_FILE_PREFIX = "LOGO_FILE_PREFIX"; public static final String STATUS_URL = "STATUS_URL"; public static final String TEMPORARY_FILE_DIRECTORY = "TEMPORARY_FILE_DIRECTORY"; diff --git a/src/main/java/org/kuali/kfs/pdp/batch/service/impl/Iso20022FormatExtractor.java b/src/main/java/org/kuali/kfs/pdp/batch/service/impl/Iso20022FormatExtractor.java index a8b3470524..ebdded26a2 100644 --- a/src/main/java/org/kuali/kfs/pdp/batch/service/impl/Iso20022FormatExtractor.java +++ b/src/main/java/org/kuali/kfs/pdp/batch/service/impl/Iso20022FormatExtractor.java @@ -161,7 +161,6 @@ public class Iso20022FormatExtractor { private static final Logger LOG = LogManager.getLogger(); - private static final String CURRENCY_USD = "USD"; private static final int REF_MAX_LENGTH = 30; diff --git a/src/main/java/org/kuali/kfs/pdp/businessobject/ACHPayee.java b/src/main/java/org/kuali/kfs/pdp/businessobject/ACHPayee.java index f78b3aa198..746490d9de 100644 --- a/src/main/java/org/kuali/kfs/pdp/businessobject/ACHPayee.java +++ b/src/main/java/org/kuali/kfs/pdp/businessobject/ACHPayee.java @@ -24,7 +24,6 @@ import org.kuali.kfs.kim.impl.identity.Person; import org.kuali.kfs.krad.service.BusinessObjectService; import org.kuali.kfs.sys.context.SpringContext; -import org.kuali.kfs.core.api.mo.common.active.MutableInactivatable; //Cornell customization: this was overlayed to add the principalName. public class ACHPayee extends DisbursementPayee implements MutableInactivatable { diff --git a/src/main/java/org/kuali/kfs/pdp/businessobject/service/ACHPayeeSearchService.java b/src/main/java/org/kuali/kfs/pdp/businessobject/service/ACHPayeeSearchService.java index 519a771e86..c209d032e1 100644 --- a/src/main/java/org/kuali/kfs/pdp/businessobject/service/ACHPayeeSearchService.java +++ b/src/main/java/org/kuali/kfs/pdp/businessobject/service/ACHPayeeSearchService.java @@ -148,6 +148,7 @@ protected DisbursementPayee getPayeeFromPerson(final Person personDetail, final return achPayee; } + @Override public void validateSearchParameters(final Map fieldValues) { final String vendorName = fieldValues.get(KFSPropertyConstants.VENDOR_NAME); final String vendorNumber = fieldValues.get(KFSPropertyConstants.VENDOR_NUMBER); @@ -223,7 +224,7 @@ protected List getVendorsAsPayees(final MultiValueMap customers = formatForm.getCustomers(); @@ -227,10 +224,9 @@ public ActionForward clear(ActionMapping mapping, ActionForm form, HttpServletRe * @param request * @param response * @return - * @throws Exception */ public ActionForward cancel(ActionMapping mapping, ActionForm form, HttpServletRequest request, - HttpServletResponse response) throws Exception { + HttpServletResponse response) { FormatForm formatForm = (FormatForm) form; KualiInteger processId = formatForm.getFormatProcessSummary().getProcessId(); @@ -250,10 +246,9 @@ public ActionForward cancel(ActionMapping mapping, ActionForm form, HttpServletR * @param request * @param response * @return - * @throws Exception */ public ActionForward clearUnfinishedFormat(ActionMapping mapping, ActionForm form, HttpServletRequest request, - HttpServletResponse response) throws Exception { + HttpServletResponse response) { String processIdParam = request.getParameter(PdpConstants.PROCESS_ID); Integer processId = Integer.parseInt(processIdParam); diff --git a/src/main/java/org/kuali/kfs/sys/context/KFSConfigurer.java b/src/main/java/org/kuali/kfs/sys/context/KFSConfigurer.java index bbe99c4121..bc6051112c 100644 --- a/src/main/java/org/kuali/kfs/sys/context/KFSConfigurer.java +++ b/src/main/java/org/kuali/kfs/sys/context/KFSConfigurer.java @@ -112,7 +112,7 @@ public KFSConfigurer() { } @Override - public final void afterPropertiesSet() throws Exception { + public final void afterPropertiesSet() { validateConfigurerState(); ensureServletContext(); initializeResourceLoaders(); @@ -158,7 +158,7 @@ private void enableSpringMvcForRestApis() { } @Override - public final void destroy() throws Exception { + public final void destroy() { doAdditionalModuleStopLogic(); // CU Customization: Shut down child Spring contexts. if (cuMvcEndpointsLoader != null) { @@ -211,7 +211,7 @@ private static List parseFileList(final String files) { return parsedFiles; } - private void initializeResourceLoaders() throws Exception { + private void initializeResourceLoaders() { GlobalResourceLoader.initialize(servletContext, getPrimarySpringFiles()); GlobalResourceLoader.start(); From 55dc702962886cc81e753f68f93ff59a3d047688 Mon Sep 17 00:00:00 2001 From: Daniela Balmus Date: Mon, 9 Feb 2026 11:37:01 +0000 Subject: [PATCH 08/12] KFSPTS-36246 add changes to overlays --- .../batch/AwardTranslationDefinition.java | 4 +- .../web/renderers/GroupTitleLineRenderer.java | 4 +- .../sys/web/struts/ErrorHandlerAction.java | 2 +- .../KualiAccountingDocumentActionBase.java | 18 ++---- .../web/struts/KualiBatchJobModifyAction.java | 10 ++-- .../vnd/dataaccess/impl/VendorDaoImpl.java | 2 +- .../datadictionary/AccountGlobal.xml | 6 -- .../AccountGlobalMaintenanceDocument.xml | 1 - .../config/MaintainableXMLUpgradeRules.xml | 55 +++++++++++++++++-- .../AwardMaintenanceDocument.xml | 4 +- .../org/kuali/kfs/core/config/spring-core.xml | 2 +- .../kfs/krad/service/impl/AwardTest.xml | 14 ++--- .../kfs/krad/service/impl/LegacyAwardTest.xml | 14 ++--- .../kfs/krad/service/impl/ProposalTest.xml | 4 +- 14 files changed, 86 insertions(+), 54 deletions(-) diff --git a/src/main/java/edu/cornell/kfs/rass/batch/AwardTranslationDefinition.java b/src/main/java/edu/cornell/kfs/rass/batch/AwardTranslationDefinition.java index ae1e9dd167..73e18318e6 100644 --- a/src/main/java/edu/cornell/kfs/rass/batch/AwardTranslationDefinition.java +++ b/src/main/java/edu/cornell/kfs/rass/batch/AwardTranslationDefinition.java @@ -75,9 +75,9 @@ public List getKeysOfUpstreamObjectUpdatesToWaitFor(RassXmlAwardEntry xm @Override public void processCustomTranslationForBusinessObjectCreate( RassXmlAwardEntry xmlAward, Award newAward) { - String proposalAwardTypeCode = parameterService.getParameterValueAsString( + String awardTypeCode = parameterService.getParameterValueAsString( RassStep.class, RassConstants.RASS_DEFAULT_PROPOSAL_AWARD_TYPE_PARAMETER); - newAward.setAwardTypeCode(proposalAwardTypeCode); + newAward.setAwardTypeCode(awardTypeCode); newAward.setAwardEntryDate(dateTimeService.getCurrentSqlDate()); newAward.getAwardAccounts().add(createDefaultAwardAccount(xmlAward)); addPrimaryFundManager(xmlAward, newAward); diff --git a/src/main/java/org/kuali/kfs/sys/document/web/renderers/GroupTitleLineRenderer.java b/src/main/java/org/kuali/kfs/sys/document/web/renderers/GroupTitleLineRenderer.java index 0700b550a2..b2aed9b923 100644 --- a/src/main/java/org/kuali/kfs/sys/document/web/renderers/GroupTitleLineRenderer.java +++ b/src/main/java/org/kuali/kfs/sys/document/web/renderers/GroupTitleLineRenderer.java @@ -242,7 +242,7 @@ protected String buildGroupActionsColumnEnding() { * * @return the String with the HTML for the title cell */ - protected String buildTitleCell() throws JspException { + protected String buildTitleCell() { final StringBuilder titleCell = new StringBuilder(); int colSpan = titleCellSpan; @@ -276,7 +276,7 @@ protected String buildTitleCell() throws JspException { return titleCell.toString(); } - protected String buildBlankCell() throws JspException { + protected String buildBlankCell() { final StringBuilder titleCell = new StringBuilder(); titleCell.append(""); return titleCell.toString(); diff --git a/src/main/java/org/kuali/kfs/sys/web/struts/ErrorHandlerAction.java b/src/main/java/org/kuali/kfs/sys/web/struts/ErrorHandlerAction.java index 64ec6347e8..bffdddc679 100644 --- a/src/main/java/org/kuali/kfs/sys/web/struts/ErrorHandlerAction.java +++ b/src/main/java/org/kuali/kfs/sys/web/struts/ErrorHandlerAction.java @@ -39,7 +39,7 @@ public class ErrorHandlerAction extends Action { private static final Logger LOG = LogManager.getLogger(); @Override - public ActionForward execute(final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, final HttpServletResponse response) throws Exception { + public ActionForward execute(final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, final HttpServletResponse response) { LOG.debug("execute() started"); final Exception exception = (Exception) request.getAttribute(Globals.EXCEPTION_KEY); diff --git a/src/main/java/org/kuali/kfs/sys/web/struts/KualiAccountingDocumentActionBase.java b/src/main/java/org/kuali/kfs/sys/web/struts/KualiAccountingDocumentActionBase.java index 036deeed25..d84d925d9a 100644 --- a/src/main/java/org/kuali/kfs/sys/web/struts/KualiAccountingDocumentActionBase.java +++ b/src/main/java/org/kuali/kfs/sys/web/struts/KualiAccountingDocumentActionBase.java @@ -237,11 +237,10 @@ protected void processAccountingLines( * @param request * @param response * @return ActionForward - * @throws Exception */ public ActionForward deleteTargetLine( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final KualiAccountingDocumentFormBase financialDocumentForm = (KualiAccountingDocumentFormBase) form; final int deleteIndex = getLineToDelete(request); @@ -432,11 +431,10 @@ protected void checkUploadFile(final FormFile file) { * @param request * @param response * @return ActionForward - * @throws Exception */ public ActionForward insertTargetLine( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final KualiAccountingDocumentFormBase financialDocumentForm = (KualiAccountingDocumentFormBase) form; final TargetAccountingLine line = financialDocumentForm.getNewTargetLine(); @@ -562,11 +560,10 @@ protected List deepCopyAccountingLinesList(final List originals) { * @param request * @param response * @return ActionForward - * @throws Exception */ public ActionForward showDetails( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final KualiAccountingDocumentFormBase tmpForm = (KualiAccountingDocumentFormBase) form; tmpForm.setHideDetails(false); return mapping.findForward(KFSConstants.MAPPING_BASIC); @@ -581,11 +578,10 @@ public ActionForward showDetails( * @param request * @param response * @return ActionForward - * @throws Exception */ public ActionForward hideDetails( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final KualiAccountingDocumentFormBase tmpForm = (KualiAccountingDocumentFormBase) form; tmpForm.setHideDetails(true); return mapping.findForward(KFSConstants.MAPPING_BASIC); @@ -600,11 +596,10 @@ public ActionForward hideDetails( * @param request * @param response * @return ActionForward - * @throws Exception */ public ActionForward performBalanceInquiryForSourceLine( final ActionMapping mapping, final ActionForm form, - final HttpServletRequest request, final HttpServletResponse response) throws Exception { + final HttpServletRequest request, final HttpServletResponse response) { final SourceAccountingLine line = getSourceAccountingLine(form, request); return performBalanceInquiryForAccountingLine(mapping, form, request, line); } @@ -618,11 +613,10 @@ public ActionForward performBalanceInquiryForSourceLine( * @param request * @param response * @return ActionForward - * @throws Exception */ public ActionForward performBalanceInquiryForTargetLine( final ActionMapping mapping, final ActionForm form, - final HttpServletRequest request, final HttpServletResponse response) throws Exception { + final HttpServletRequest request, final HttpServletResponse response) { final TargetAccountingLine line = getTargetAccountingLine(form, request); return performBalanceInquiryForAccountingLine(mapping, form, request, line); } diff --git a/src/main/java/org/kuali/kfs/sys/web/struts/KualiBatchJobModifyAction.java b/src/main/java/org/kuali/kfs/sys/web/struts/KualiBatchJobModifyAction.java index e62850f734..3bafc541cc 100644 --- a/src/main/java/org/kuali/kfs/sys/web/struts/KualiBatchJobModifyAction.java +++ b/src/main/java/org/kuali/kfs/sys/web/struts/KualiBatchJobModifyAction.java @@ -204,7 +204,7 @@ private static String buildAuthHeader(final HttpServletRequest request) { public ActionForward start( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final KualiBatchJobModifyForm batchModifyForm = (KualiBatchJobModifyForm) form; request.setAttribute("job", batchModifyForm.getJob()); @@ -219,7 +219,7 @@ public ActionForward start( public ActionForward runJob( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final KualiBatchJobModifyForm batchModifyForm = (KualiBatchJobModifyForm) form; checkJobAuthorization(batchModifyForm, "runJob"); @@ -264,7 +264,7 @@ public ActionForward runJob( public ActionForward stopJob( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final KualiBatchJobModifyForm batchModifyForm = (KualiBatchJobModifyForm) form; checkJobAuthorization(batchModifyForm, "stopJob"); @@ -276,7 +276,7 @@ public ActionForward stopJob( public ActionForward schedule( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final KualiBatchJobModifyForm batchModifyForm = (KualiBatchJobModifyForm) form; checkJobAuthorization(batchModifyForm, "schedule"); @@ -288,7 +288,7 @@ public ActionForward schedule( public ActionForward unschedule( final ActionMapping mapping, final ActionForm form, final HttpServletRequest request, - final HttpServletResponse response) throws Exception { + final HttpServletResponse response) { final KualiBatchJobModifyForm batchModifyForm = (KualiBatchJobModifyForm) form; checkJobAuthorization(batchModifyForm, "unschedule"); diff --git a/src/main/java/org/kuali/kfs/vnd/dataaccess/impl/VendorDaoImpl.java b/src/main/java/org/kuali/kfs/vnd/dataaccess/impl/VendorDaoImpl.java index 03dd08a694..2d229d6383 100644 --- a/src/main/java/org/kuali/kfs/vnd/dataaccess/impl/VendorDaoImpl.java +++ b/src/main/java/org/kuali/kfs/vnd/dataaccess/impl/VendorDaoImpl.java @@ -594,7 +594,7 @@ private String addSortAndLimitSql( .append(sortAscending ? " ASC " : " DESC "); } - // CU Customization: Backport FINP-11585 + // CU Customization: update SQL for Oracle compatibility orderedAndLimited.append("OFFSET ").append(skip).append(" ROWS FETCH NEXT ").append(limit).append(" ROWS ONLY "); return orderedAndLimited.toString(); } diff --git a/src/main/resources/edu/cornell/kfs/coa/businessobject/datadictionary/AccountGlobal.xml b/src/main/resources/edu/cornell/kfs/coa/businessobject/datadictionary/AccountGlobal.xml index 4f637341ab..9a6d753f85 100644 --- a/src/main/resources/edu/cornell/kfs/coa/businessobject/datadictionary/AccountGlobal.xml +++ b/src/main/resources/edu/cornell/kfs/coa/businessobject/datadictionary/AccountGlobal.xml @@ -29,7 +29,6 @@ - @@ -157,11 +156,6 @@ parent="Account-acctIndirectCostRcvyTypeCd"> - - - - diff --git a/src/main/resources/edu/cornell/kfs/coa/document/datadictionary/AccountGlobalMaintenanceDocument.xml b/src/main/resources/edu/cornell/kfs/coa/document/datadictionary/AccountGlobalMaintenanceDocument.xml index db24f29e17..6a7b844c85 100644 --- a/src/main/resources/edu/cornell/kfs/coa/document/datadictionary/AccountGlobalMaintenanceDocument.xml +++ b/src/main/resources/edu/cornell/kfs/coa/document/datadictionary/AccountGlobalMaintenanceDocument.xml @@ -251,7 +251,6 @@ - diff --git a/src/main/resources/edu/cornell/kfs/krad/config/MaintainableXMLUpgradeRules.xml b/src/main/resources/edu/cornell/kfs/krad/config/MaintainableXMLUpgradeRules.xml index 06900856f2..7c79ff91f8 100644 --- a/src/main/resources/edu/cornell/kfs/krad/config/MaintainableXMLUpgradeRules.xml +++ b/src/main/resources/edu/cornell/kfs/krad/config/MaintainableXMLUpgradeRules.xml @@ -508,17 +508,25 @@ org.kuali.kfs.module.cg.businessobject.Proposal - userLookupRoleNamespaceCode - + proposalAwardType + awardType - userLookupRoleName - + proposalAwardTypeCode + awardTypeCode statusCode + + userLookupRoleName + + + + userLookupRoleNamespaceCode + + proposal @@ -1224,7 +1232,7 @@ org.kuali.rice.kns.bo.KualiCodeBase - org.kuali.kfs.krad.bo.KualiCodeBase + org.kuali.kfs.krad.bo.CodedBase org.kuali.rice.krad.bo.KualiCodeBase @@ -1608,6 +1616,43 @@ + + org.kuali.kfs.module.ld.businessobject.LaborObject + + detailPositionRequiredIndicator + + + + financialObjectHoursRequiredIndicator + + + + financialObjectPayTypeCode + + + + + org.kuali.kfs.module.cg.businessobject.AwardType + + proposalAwardTypeCode + awardTypeCode + + + proposalAwardTypeDescription + awardTypeDescription + + + + org.kuali.kfs.module.cg.businessobject.Award + + proposalAwardType + awardType + + + proposalAwardTypeCode + awardTypeCode + + 2.18.1 3.0.3 @@ -55,12 +55,12 @@ 2.25.1 2.1.10-kuali-1 1.1.1 - 5.11.0 + 5.11.3 19.18.0.0 5.9 3.0.24 - 5.13.0 - SRU2023-10.1.7 + 5.14.2 + SRU2024-10.2.3 1.3.18 5.3.39 1.5.0-patch7 @@ -145,7 +145,7 @@ - -Xms1024m -Xmx1024m -Doracle.jdbc.DateZeroTime=true + -Doracle.jdbc.DateZeroTime=true ${test.excludes} diff --git a/src/main/java/edu/cornell/kfs/module/purap/service/impl/CuPurapGeneralLedgerServiceImpl.java b/src/main/java/edu/cornell/kfs/module/purap/service/impl/CuPurapGeneralLedgerServiceImpl.java index 4a3a99a702..db87b2dfe6 100644 --- a/src/main/java/edu/cornell/kfs/module/purap/service/impl/CuPurapGeneralLedgerServiceImpl.java +++ b/src/main/java/edu/cornell/kfs/module/purap/service/impl/CuPurapGeneralLedgerServiceImpl.java @@ -220,6 +220,9 @@ protected boolean generateEntriesPaymentRequest( } preq.generateDocumentGeneralLedgerPendingEntries(sequenceHelper); // End: Cornell Customization: KFSPTS-1891 + } else if (MODIFY_PAYMENT_REQUEST.equals(processType) && itemsChanged(preq.getItems())) { + purapAccountRevisionService.savePaymentRequestAccountRevisions(preq.getItems(), + preq.getPostingYearFromPendingGLEntries(), preq.getPostingPeriodCodeFromPendingGLEntries()); } // Manually save GL entries for Payment Request and encumbrances diff --git a/src/main/java/edu/cornell/kfs/sys/batch/CuJobListener.java b/src/main/java/edu/cornell/kfs/sys/batch/CuJobListener.java index f235b12faa..78bd102dad 100644 --- a/src/main/java/edu/cornell/kfs/sys/batch/CuJobListener.java +++ b/src/main/java/edu/cornell/kfs/sys/batch/CuJobListener.java @@ -39,7 +39,14 @@ protected void notify(final JobExecutionContext jobExecutionContext, final Strin mailMessage.setMessage(messageText); if (mailMessage.getToAddresses().size() > 0) { mailMessage.setSubject(mailMessageSubject.toString()); - emailService.sendMessage(mailMessage, false); + if (SchedulerService.FAILED_JOB_STATUS_CODE.equals(jobStatus) + || SchedulerService.CANCELLED_JOB_STATUS_CODE.equals(jobStatus) + ) { + // Send the message synchronously as the transaction will (likely) be rolled back + emailService.transportMessage(mailMessage, false); + } else { + emailService.sendMessage(mailMessage, false); + } } } catch (final Exception iae) { LOG.error( diff --git a/src/main/java/edu/cornell/kfs/sys/batch/service/impl/CuAutoDisapproveDocumentsServiceImpl.java b/src/main/java/edu/cornell/kfs/sys/batch/service/impl/CuAutoDisapproveDocumentsServiceImpl.java index 5385d67bc7..eb6095c96e 100644 --- a/src/main/java/edu/cornell/kfs/sys/batch/service/impl/CuAutoDisapproveDocumentsServiceImpl.java +++ b/src/main/java/edu/cornell/kfs/sys/batch/service/impl/CuAutoDisapproveDocumentsServiceImpl.java @@ -337,7 +337,7 @@ protected boolean checkIfDocumentTypesExceptionParameterExists() { } protected void autoDisapprovalYearEndDocument(final Document document, - final String annotationForAutoDisapprovalDocument) throws Exception { + final String annotationForAutoDisapprovalDocument) { final Person systemUser = getPersonService().getPersonByPrincipalName(KFSConstants.SYSTEM_USER); final Note approveNote = getNoteService().createNote(new Note(), document.getDocumentHeader(), systemUser.getPrincipalId()); diff --git a/src/main/java/org/kuali/kfs/module/purap/service/impl/PurapGeneralLedgerServiceImpl.java b/src/main/java/org/kuali/kfs/module/purap/service/impl/PurapGeneralLedgerServiceImpl.java new file mode 100644 index 0000000000..dccd2fd900 --- /dev/null +++ b/src/main/java/org/kuali/kfs/module/purap/service/impl/PurapGeneralLedgerServiceImpl.java @@ -0,0 +1,1778 @@ +/* + * The Kuali Financial System, a comprehensive financial management system for higher education. + * + * Copyright 2005-2024 Kuali, Inc. + * + * 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 . + */ +package org.kuali.kfs.module.purap.service.impl; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.kuali.kfs.coa.businessobject.ObjectCode; +import org.kuali.kfs.coa.service.ObjectCodeService; +import org.kuali.kfs.core.api.datetime.DateTimeService; +import org.kuali.kfs.core.api.util.type.KualiDecimal; +import org.kuali.kfs.krad.service.BusinessObjectService; +import org.kuali.kfs.krad.util.ObjectUtils; +import org.kuali.kfs.module.purap.PurapConstants; +import org.kuali.kfs.module.purap.PurapConstants.PurapDocTypeCodes; +import org.kuali.kfs.module.purap.PurchaseOrderStatuses; +import org.kuali.kfs.module.purap.businessobject.AccountsPayableSummaryAccount; +import org.kuali.kfs.module.purap.businessobject.CreditMemoItem; +import org.kuali.kfs.module.purap.businessobject.ItemType; +import org.kuali.kfs.module.purap.businessobject.PaymentRequestAccountRevision; +import org.kuali.kfs.module.purap.businessobject.PaymentRequestItem; +import org.kuali.kfs.module.purap.businessobject.PurApAccountingLine; +import org.kuali.kfs.module.purap.businessobject.PurchaseOrderAccount; +import org.kuali.kfs.module.purap.businessobject.PurchaseOrderItem; +import org.kuali.kfs.module.purap.document.AccountsPayableDocument; +import org.kuali.kfs.module.purap.document.PaymentRequestDocument; +import org.kuali.kfs.module.purap.document.PurchaseOrderDocument; +import org.kuali.kfs.module.purap.document.PurchasingAccountsPayableDocument; +import org.kuali.kfs.module.purap.document.VendorCreditMemoDocument; +import org.kuali.kfs.module.purap.document.service.PaymentRequestService; +import org.kuali.kfs.module.purap.document.service.PurchaseOrderService; +import org.kuali.kfs.module.purap.service.PurapAccountRevisionService; +import org.kuali.kfs.module.purap.service.PurapAccountingService; +import org.kuali.kfs.module.purap.service.PurapGeneralLedgerService; +import org.kuali.kfs.module.purap.util.SummaryAccount; +import org.kuali.kfs.module.purap.util.UseTaxContainer; +import org.kuali.kfs.sys.KFSConstants; +import org.kuali.kfs.sys.businessobject.AccountingLine; +import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntry; +import org.kuali.kfs.sys.businessobject.GeneralLedgerPendingEntrySequenceHelper; +import org.kuali.kfs.sys.businessobject.SourceAccountingLine; +import org.kuali.kfs.sys.businessobject.UniversityDate; +import org.kuali.kfs.sys.service.GeneralLedgerPendingEntryService; +import org.kuali.kfs.sys.service.UniversityDateService; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +//CU customization: change method access from private to protected +@Transactional +public class PurapGeneralLedgerServiceImpl implements PurapGeneralLedgerService { + + private static final Logger LOG = LogManager.getLogger(); + + private BusinessObjectService businessObjectService; + private DateTimeService dateTimeService; + private GeneralLedgerPendingEntryService generalLedgerPendingEntryService; + private PaymentRequestService paymentRequestService; + private PurapAccountingService purapAccountingService; + private PurchaseOrderService purchaseOrderService; + private UniversityDateService universityDateService; + private ObjectCodeService objectCodeService; + private PurapAccountRevisionService purapAccountRevisionService; + + /** + * This method sets various fields in explicitEntry based on other parameters. + */ + @Override + public void customizeGeneralLedgerPendingEntry( + final PurchasingAccountsPayableDocument purapDocument, + final AccountingLine accountingLine, final GeneralLedgerPendingEntry explicitEntry, final Integer referenceDocumentNumber, + final String debitCreditCode, final String docType, final boolean isEncumbrance) { + LOG.debug("customizeGeneralLedgerPendingEntry() started"); + + explicitEntry.setDocumentNumber(purapDocument.getDocumentNumber()); + explicitEntry.setTransactionLedgerEntryDescription(entryDescription(purapDocument.getVendorName())); + explicitEntry.setFinancialSystemOriginationCode(PurapConstants.PURAP_ORIGIN_CODE); + + // Always make the referring document the PO for all PURAP docs except for CM against a vendor. + // This is required for encumbrance entries. It's not required for actual/liability + // entries, but it makes things easier to deal with. If vendor, leave referring stuff blank. + if (ObjectUtils.isNotNull(referenceDocumentNumber)) { + explicitEntry.setReferenceFinancialDocumentNumber(referenceDocumentNumber.toString()); + explicitEntry.setReferenceFinancialDocumentTypeCode( + PurapDocTypeCodes.PURCHASE_ORDER_DOCUMENT); + explicitEntry.setReferenceFinancialSystemOriginationCode(PurapConstants.PURAP_ORIGIN_CODE); + } + + // DEFAULT TO USE CURRENT; don't use FY on doc in case it's a prior year + final UniversityDate uDate = universityDateService.getCurrentUniversityDate(); + explicitEntry.setUniversityFiscalYear(uDate.getUniversityFiscalYear()); + explicitEntry.setUniversityFiscalPeriodCode(uDate.getUniversityFiscalAccountingPeriod()); + + if (PurapDocTypeCodes.PURCHASE_ORDER_DOCUMENT.equals(docType)) { + if (purapDocument.getPostingYear().compareTo(uDate.getUniversityFiscalYear()) > 0) { + // USE NEXT AS SET ON PO; POs can be forward dated to not encumber until next fiscal year + explicitEntry.setUniversityFiscalYear(purapDocument.getPostingYear()); + explicitEntry.setUniversityFiscalPeriodCode(KFSConstants.MONTH1); + } + } else if (PurapDocTypeCodes.PAYMENT_REQUEST_DOCUMENT.equals(docType)) { + final PaymentRequestDocument preq = (PaymentRequestDocument) purapDocument; + if (paymentRequestService.allowBackpost(preq)) { + LOG.debug("createGlPendingTransaction() within range to allow backpost; posting entry to " + + "period 12 of previous FY"); + explicitEntry.setUniversityFiscalYear(uDate.getUniversityFiscalYear() - 1); + explicitEntry.setUniversityFiscalPeriodCode(KFSConstants.MONTH12); + } + + // if alternate payee is paid for non-primary vendor payment, send alternate vendor name in GL desc + if (preq.getAlternateVendorHeaderGeneratedIdentifier() != null + && preq.getAlternateVendorDetailAssignedIdentifier() != null + && preq.getVendorHeaderGeneratedIdentifier().compareTo( + preq.getAlternateVendorHeaderGeneratedIdentifier()) == 0 + && preq.getVendorDetailAssignedIdentifier().compareTo( + preq.getAlternateVendorDetailAssignedIdentifier()) == 0) { + explicitEntry.setTransactionLedgerEntryDescription(entryDescription( + preq.getPurchaseOrderDocument().getAlternateVendorName())); + } + } else if (PurapDocTypeCodes.CREDIT_MEMO_DOCUMENT.equals(docType)) { + final VendorCreditMemoDocument cm = (VendorCreditMemoDocument) purapDocument; + if (cm.isSourceDocumentPaymentRequest()) { + // if CM is off of PREQ, use vendor name associated with PO (if alternate) + final PaymentRequestDocument cmPR = cm.getPaymentRequestDocument(); + // if alternate payee is paid for non-primary vendor payment, send alternate vendor name in GL desc + if (cmPR.getAlternateVendorHeaderGeneratedIdentifier() != null + && cmPR.getAlternateVendorDetailAssignedIdentifier() != null + && cmPR.getVendorHeaderGeneratedIdentifier().compareTo( + cmPR.getAlternateVendorHeaderGeneratedIdentifier()) == 0 + && cmPR.getVendorDetailAssignedIdentifier().compareTo( + cmPR.getAlternateVendorDetailAssignedIdentifier()) == 0) { + final PurchaseOrderDocument cmPO = cm.getPurchaseOrderDocument(); + explicitEntry.setTransactionLedgerEntryDescription(entryDescription( + cmPO.getAlternateVendorName())); + } + } + } else { + throw new IllegalArgumentException("purapDocument is invalid doc type: " + + purapDocument.getDocumentNumber()); + } + + final ObjectCode objectCode = objectCodeService.getByPrimaryId(explicitEntry.getUniversityFiscalYear(), + explicitEntry.getChartOfAccountsCode(), explicitEntry.getFinancialObjectCode()); + if (ObjectUtils.isNotNull(objectCode)) { + explicitEntry.setFinancialObjectTypeCode(objectCode.getFinancialObjectTypeCode()); + } + + if (isEncumbrance) { + explicitEntry.setFinancialBalanceTypeCode(KFSConstants.BALANCE_TYPE_EXTERNAL_ENCUMBRANCE); + + // D - means the encumbrance is based on the document number + // R - means the encumbrance is based on the referring document number + // All encumbrances should set the update code to 'R' regardless of if they were created by the PO, PREQ, + // or CM + explicitEntry.setTransactionEncumbranceUpdateCode(KFSConstants.ENCUMB_UPDT_REFERENCE_DOCUMENT_CD); + } + + // if the amount is negative, flip the D/C indicator + if (accountingLine.getAmount().doubleValue() < 0) { + if (KFSConstants.GL_CREDIT_CODE.equals(debitCreditCode)) { + explicitEntry.setTransactionDebitCreditCode(KFSConstants.GL_DEBIT_CODE); + } else { + explicitEntry.setTransactionDebitCreditCode(KFSConstants.GL_CREDIT_CODE); + } + } else { + explicitEntry.setTransactionDebitCreditCode(debitCreditCode); + } + } + + @Override + public void generateEntriesCancelAccountsPayableDocument(final AccountsPayableDocument apDocument) { + LOG.debug("generateEntriesCancelAccountsPayableDocument() started"); + if (apDocument instanceof PaymentRequestDocument) { + LOG.info("generateEntriesCancelAccountsPayableDocument() cancel PaymentRequestDocument"); + generateEntriesCancelPaymentRequest((PaymentRequestDocument) apDocument); + } else if (apDocument instanceof VendorCreditMemoDocument) { + LOG.info("generateEntriesCancelAccountsPayableDocument() cancel CreditMemoDocument"); + generateEntriesCancelCreditMemo((VendorCreditMemoDocument) apDocument); + } + } + + @Override + public void generateEntriesCreatePaymentRequest(final PaymentRequestDocument preq) { + LOG.debug("generateEntriesCreatePaymentRequest() started"); + final List encumbrances = relieveEncumbrance(preq); + final List summaryAccounts = purapAccountingService + .generateSummaryAccountsWithNoZeroTotalsNoUseTax(preq); + generateEntriesPaymentRequest(preq, encumbrances, summaryAccounts, CREATE_PAYMENT_REQUEST); + } + + /** + * Called from generateEntriesCancelAccountsPayableDocument() for Payment Request Document + * + * @param preq Payment Request document to cancel + */ + protected void generateEntriesCancelPaymentRequest(final PaymentRequestDocument preq) { + LOG.debug("generateEntriesCreatePaymentRequest() started"); + final List encumbrances = reencumberEncumbrance(preq); + final List summaryAccounts = purapAccountingService + .generateSummaryAccountsWithNoZeroTotalsNoUseTax(preq); + generateEntriesPaymentRequest(preq, encumbrances, summaryAccounts, CANCEL_PAYMENT_REQUEST); + } + + @Override + public void generateEntriesModifyPaymentRequest(final PaymentRequestDocument preq) { + LOG.debug("generateEntriesModifyPaymentRequest() started"); + + final Map actualsPositive = new HashMap<>(); + final List newAccountingLines = + purapAccountingService.generateSummaryWithNoZeroTotalsNoUseTax(preq.getItems()); + for (final SourceAccountingLine newAccount : newAccountingLines) { + actualsPositive.put(newAccount, newAccount.getAmount()); + LOG.debug( + "generateEntriesModifyPaymentRequest() actualsPositive: {} = {}", + newAccount::getAccountNumber, + newAccount::getAmount + ); + } + + final Map actualsNegative = new HashMap<>(); + final List oldAccountingLines = + purapAccountingService.getAccountsPayableSummaryAccounts(preq.getPurapDocumentIdentifier(), + PurapDocTypeCodes.PAYMENT_REQUEST_DOCUMENT); + + for (final AccountsPayableSummaryAccount oldAccount : oldAccountingLines) { + actualsNegative.put(oldAccount.generateSourceAccountingLine(), oldAccount.getAmount()); + LOG.debug( + "generateEntriesModifyPaymentRequest() actualsNegative: {} = {}", + oldAccount::getAccountNumber, + oldAccount::getAmount + ); + } + + // Add the positive entries and subtract the negative entries + + // Combine the two maps (copy all the positive entries) + LOG.debug("generateEntriesModifyPaymentRequest() Combine positive/negative entries"); + final Map glEntries = new HashMap<>(actualsPositive); + + for (final SourceAccountingLine key : actualsNegative.keySet()) { + KualiDecimal amt; + if (glEntries.containsKey(key)) { + amt = glEntries.get(key); + amt = amt.subtract(actualsNegative.get(key)); + } else { + amt = KualiDecimal.ZERO; + amt = amt.subtract(actualsNegative.get(key)); + } + glEntries.put(key, amt); + } + + final List summaryAccounts = new ArrayList<>(); + for (final SourceAccountingLine account : glEntries.keySet()) { + final KualiDecimal amount = glEntries.get(account); + if (KualiDecimal.ZERO.compareTo(amount) != 0) { + account.setAmount(amount); + final SummaryAccount sa = new SummaryAccount(account); + summaryAccounts.add(sa); + } + } + + LOG.debug("generateEntriesModifyPaymentRequest() Generate GL entries"); + generateEntriesPaymentRequest(preq, null, summaryAccounts, MODIFY_PAYMENT_REQUEST); + } + + @Override + public void generateEntriesProcessedPaymentRequest(final PaymentRequestDocument preq) { + LOG.debug("generateEntriesProcessedPaymentRequest(...) - Enter"); + final boolean isExternal = + KFSConstants.PaymentSourceConstants.PAYMENT_METHOD_EXTERNAL.equals(preq.getPaymentMethodCode()); + final boolean isWireTransfer = + KFSConstants.PaymentSourceConstants.PAYMENT_METHOD_WIRE.equals(preq.getPaymentMethodCode()); + if (!isExternal && !isWireTransfer) { + LOG.debug( + "generateEntriesProcessedPaymentRequest(...) - Exit: payment method is {}, doing nothing", + preq::getPaymentMethodCode + ); + return; + } + + final List summaryAccounts = + purapAccountingService.generateSummaryAccountsWithNoZeroTotalsNoUseTax(preq); + preq.setDebitCreditCodeForGLEntries(KFSConstants.GL_CREDIT_CODE); + LOG.debug("generateEntriesProcessedPaymentRequest(...) - credit {} accounts for wire transfer", + summaryAccounts::size); + generateEntriesPaymentRequest(preq, null, summaryAccounts, PROCESS_PAYMENT_REQUEST); + + preq.setDebitCreditCodeForGLEntries(KFSConstants.GL_DEBIT_CODE); + preq.setGenerateExternalEntries(isExternal); + preq.setGenerateWireTransferEntries(isWireTransfer); + LOG.debug( + "generateEntriesProcessedPaymentRequest(...) - debit {} accounts for {}", + summaryAccounts::size, + () -> isExternal ? "external" : "wire transfer" + ); + generateEntriesPaymentRequest(preq, null, summaryAccounts, PROCESS_PAYMENT_REQUEST); + preq.setGenerateExternalEntries(false); + preq.setGenerateWireTransferEntries(false); + LOG.debug("generateEntriesProcessedPaymentRequest(...) - Exit"); + } + + @Override + public void generateEntriesCreateCreditMemo(final VendorCreditMemoDocument cm) { + LOG.debug("generateEntriesCreateCreditMemo() started"); + generateEntriesCreditMemo(cm, CREATE_CREDIT_MEMO); + } + + /** + * Called from generateEntriesCancelAccountsPayableDocument() for Payment Request Document + * + * @param cm + */ + protected void generateEntriesCancelCreditMemo(final VendorCreditMemoDocument cm) { + LOG.debug("generateEntriesCancelCreditMemo() started"); + generateEntriesCreditMemo(cm, CANCEL_CREDIT_MEMO); + } + + /** + * Retrieves the next available sequence number from the general ledger pending entry table for this document + * + * @param documentNumber Document number to find next sequence number + * @return Next available sequence number + */ + protected int getNextAvailableSequence(final String documentNumber) { + LOG.debug("getNextAvailableSequence() started"); + final Map fieldValues = new HashMap<>(); + fieldValues.put("financialSystemOriginationCode", PurapConstants.PURAP_ORIGIN_CODE); + fieldValues.put("documentNumber", documentNumber); + final int count = businessObjectService.countMatching(GeneralLedgerPendingEntry.class, fieldValues); + return count + 1; + } + + /** + * Creates the general ledger entries for Payment Request actions. + * + * @param preq Payment Request document to create entries + * @param encumbrances List of encumbrance accounts if applies + * @param summaryAccounts List of preq accounts to create entries + * @param processType Type of process (create, modify, cancel) + * @return Boolean returned indicating whether entry creation succeeded + */ + protected boolean generateEntriesPaymentRequest( + final PaymentRequestDocument preq, final List encumbrances, + final List summaryAccounts, final String processType) { + LOG.debug("generateEntriesPaymentRequest() started"); + final boolean success = true; + preq.setGeneralLedgerPendingEntries(new ArrayList<>()); + + /* + * Can't let generalLedgerPendingEntryService just create all the entries because we need the sequenceHelper + * to carry over from the encumbrances to the actuals and also because we need to tell the + * PaymentRequestDocumentRule customize entry method how to customize differently based on if creating an + * encumbrance or actual. + */ + final GeneralLedgerPendingEntrySequenceHelper sequenceHelper = new GeneralLedgerPendingEntrySequenceHelper( + getNextAvailableSequence(preq.getDocumentNumber())); + + // when cancelling a PREQ, do not book encumbrances if PO is CLOSED + if (encumbrances != null && !(CANCEL_PAYMENT_REQUEST.equals(processType) + && PurchaseOrderStatuses.APPDOC_CLOSED.equals( + preq.getPurchaseOrderDocument().getApplicationDocumentStatus()))) { + LOG.debug("generateEntriesPaymentRequest() generate encumbrance entries"); + if (CREATE_PAYMENT_REQUEST.equals(processType)) { + // on create, use CREDIT code for encumbrances + preq.setDebitCreditCodeForGLEntries(KFSConstants.GL_CREDIT_CODE); + } else if (CANCEL_PAYMENT_REQUEST.equals(processType)) { + // on cancel, use DEBIT code + preq.setDebitCreditCodeForGLEntries(KFSConstants.GL_DEBIT_CODE); + } + + preq.setGenerateEncumbranceEntries(true); + for (final Object encumbrance : encumbrances) { + final AccountingLine accountingLine = (AccountingLine) encumbrance; + preq.generateGeneralLedgerPendingEntries(accountingLine, sequenceHelper); + sequenceHelper.increment(); + } + } + + if (ObjectUtils.isNotNull(summaryAccounts) && !summaryAccounts.isEmpty()) { + LOG.debug("generateEntriesPaymentRequest() now book the actuals"); + preq.setGenerateEncumbranceEntries(false); + + if (CREATE_PAYMENT_REQUEST.equals(processType) || MODIFY_PAYMENT_REQUEST.equals(processType)) { + // on create and modify, use DEBIT code + preq.setDebitCreditCodeForGLEntries(KFSConstants.GL_DEBIT_CODE); + } else if (CANCEL_PAYMENT_REQUEST.equals(processType)) { + // on cancel, use CREDIT code + preq.setDebitCreditCodeForGLEntries(KFSConstants.GL_CREDIT_CODE); + + preq.setGenerateExternalEntries(cancellingShouldReverseExternalEntries(preq)); + preq.setGenerateWireTransferEntries(cancellingShouldReverseWireTransferEntries(preq)); + } + + for (final Object account : summaryAccounts) { + final SummaryAccount summaryAccount = (SummaryAccount) account; + preq.generateGeneralLedgerPendingEntries(summaryAccount.getAccount(), sequenceHelper); + sequenceHelper.increment(); + } + + preq.setGenerateExternalEntries(false); + preq.setGenerateWireTransferEntries(false); + + // generate offset accounts for use tax if it exists (useTaxContainers will be empty if not a use tax + // document) + final List useTaxContainers = purapAccountingService.generateUseTaxAccount(preq); + for (final UseTaxContainer useTaxContainer : useTaxContainers) { + final List accounts = useTaxContainer.getAccounts(); + for (final SourceAccountingLine sourceAccountingLine : accounts) { + preq.generateGeneralLedgerPendingEntries(sourceAccountingLine, sequenceHelper, + useTaxContainer.getUseTax()); + sequenceHelper.increment(); + } + + } + + // Manually save preq summary accounts + if (MODIFY_PAYMENT_REQUEST.equals(processType)) { + //for modify, regenerate the summary from the doc + final List summaryAccountsForModify = + purapAccountingService.generateSummaryAccountsWithNoZeroTotalsNoUseTax(preq); + saveAccountsPayableSummaryAccounts(summaryAccountsForModify, preq.getPurapDocumentIdentifier(), + PurapDocTypeCodes.PAYMENT_REQUEST_DOCUMENT); + } else { + //for create, process and cancel, use the summary accounts + saveAccountsPayableSummaryAccounts(summaryAccounts, preq.getPurapDocumentIdentifier(), + PurapDocTypeCodes.PAYMENT_REQUEST_DOCUMENT); + } + + // manually save cm account change tables (CAMS needs this) + if (CREATE_PAYMENT_REQUEST.equals(processType) + || MODIFY_PAYMENT_REQUEST.equals(processType) + || PROCESS_PAYMENT_REQUEST.equals(processType) + ) { + purapAccountRevisionService.savePaymentRequestAccountRevisions(preq.getItems(), + preq.getPostingYearFromPendingGLEntries(), preq.getPostingPeriodCodeFromPendingGLEntries()); + } else if (CANCEL_PAYMENT_REQUEST.equals(processType)) { + purapAccountRevisionService.cancelPaymentRequestAccountRevisions(preq.getItems(), + preq.getPostingYearFromPendingGLEntries(), preq.getPostingPeriodCodeFromPendingGLEntries()); + } + } else if (MODIFY_PAYMENT_REQUEST.equals(processType) && itemsChanged(preq.getItems())) { + purapAccountRevisionService.savePaymentRequestAccountRevisions(preq.getItems(), + preq.getPostingYearFromPendingGLEntries(), preq.getPostingPeriodCodeFromPendingGLEntries()); + } + + // Manually save GL entries for Payment Request and encumbrances + saveGLEntries(preq.getGeneralLedgerPendingEntries()); + + return success; + } + + //CU customization: change method access from private to protected + protected boolean itemsChanged(final List items) { + final List itemIdentifiers = items.stream() + .map(PaymentRequestItem::getItemIdentifier) + .collect(Collectors.toList()); + + final int matchingAccountRevisions = businessObjectService.countMatching( + PaymentRequestAccountRevision.class, + Map.of("itemIdentifier", itemIdentifiers) + ); + + return items.size() != matchingAccountRevisions; + } + + /* + * When a WT PREQ is entering PROCESSED in the workflow, reversing entries are creating for the PREQ actuals and + * then forward entries are generated as the PRQW actuals (see generateEntriesProcessedPaymentRequest()). + * So for a WT PREQ, if it is processed/final, we need to be generating PRQW entries to cancel the existing PRQW + * actuals instead of reversing entries for the PREQ actuals. + */ + private static boolean cancellingShouldReverseWireTransferEntries(final PaymentRequestDocument preq) { + return KFSConstants.PaymentSourceConstants.PAYMENT_METHOD_WIRE.equals(preq.getPaymentMethodCode()) + && ( + preq.getDocumentHeader().getWorkflowDocument().isProcessed() + || preq.getDocumentHeader().getWorkflowDocument().isFinal() + ); + } + + /* + * When an external payment PREQ is entering PROCESSED in the workflow, reversing entries are creating for the PREQ + * actuals and then forward entries are generated as the PRQX actuals (see generateEntriesProcessedPaymentRequest). + * So for an external payment PREQ, if it is processed/final, we need to be generating PRQX entries to cancel + * the existing PRQX actuals instead of reversing entries for the PREQ actuals. + */ + private static boolean cancellingShouldReverseExternalEntries(final PaymentRequestDocument preq) { + return KFSConstants.PaymentSourceConstants.PAYMENT_METHOD_EXTERNAL.equals(preq.getPaymentMethodCode()) + && ( + preq.getDocumentHeader().getWorkflowDocument().isProcessed() + || preq.getDocumentHeader().getWorkflowDocument().isFinal() + ); + } + + /** + * Creates the general ledger entries for Credit Memo actions. + * + * @param cm Credit Memo document to create entries + * @param isCancel Indicates if request is a cancel or create + * @return Boolean returned indicating whether entry creation succeeded + */ + protected boolean generateEntriesCreditMemo(final VendorCreditMemoDocument cm, final boolean isCancel) { + LOG.debug("generateEntriesCreditMemo() started"); + + cm.setGeneralLedgerPendingEntries(new ArrayList<>()); + + final GeneralLedgerPendingEntrySequenceHelper sequenceHelper = new GeneralLedgerPendingEntrySequenceHelper( + getNextAvailableSequence(cm.getDocumentNumber())); + + if (!cm.isSourceVendor()) { + LOG.debug("generateEntriesCreditMemo() create encumbrance entries for CM against a PO or PREQ (not " + + "vendor)"); + PurchaseOrderDocument po = null; + if (cm.isSourceDocumentPurchaseOrder()) { + LOG.debug("generateEntriesCreditMemo() PO type"); + po = purchaseOrderService.getCurrentPurchaseOrder(cm.getPurchaseOrderIdentifier()); + } else if (cm.isSourceDocumentPaymentRequest()) { + LOG.debug("generateEntriesCreditMemo() PREQ type"); + po = purchaseOrderService.getCurrentPurchaseOrder(cm.getPaymentRequestDocument() + .getPurchaseOrderIdentifier()); + } + + // for CM cancel or create, do not book encumbrances if PO is CLOSED, but do update the amounts on the PO + final List encumbrances = getCreditMemoEncumbrance(cm, po, isCancel); + if (!PurchaseOrderStatuses.APPDOC_CLOSED.equals(po.getApplicationDocumentStatus())) { + if (encumbrances != null) { + cm.setGenerateEncumbranceEntries(true); + + // even if generating encumbrance entries on cancel, call is the same because the method gets + // negative amounts from the map so Debits on negatives = a credit + cm.setDebitCreditCodeForGLEntries(KFSConstants.GL_DEBIT_CODE); + + for (final SourceAccountingLine accountingLine : encumbrances) { + if (accountingLine.getAmount().compareTo(KualiDecimal.ZERO) != 0) { + cm.generateGeneralLedgerPendingEntries(accountingLine, sequenceHelper); + sequenceHelper.increment(); + } + } + } + } + } + + final List summaryAccounts = purapAccountingService + .generateSummaryAccountsWithNoZeroTotalsNoUseTax(cm); + if (summaryAccounts != null) { + LOG.debug("generateEntriesCreditMemo() now book the actuals"); + cm.setGenerateEncumbranceEntries(false); + + if (!isCancel) { + // on create, use CREDIT code + cm.setDebitCreditCodeForGLEntries(KFSConstants.GL_CREDIT_CODE); + } else { + // on cancel, use DEBIT code + cm.setDebitCreditCodeForGLEntries(KFSConstants.GL_DEBIT_CODE); + } + + for (final SummaryAccount summaryAccount : summaryAccounts) { + cm.generateGeneralLedgerPendingEntries(summaryAccount.getAccount(), sequenceHelper); + sequenceHelper.increment(); + } + // generate offset accounts for use tax if it exists (useTaxContainers will be empty if not a use tax + // document) + final List useTaxContainers = purapAccountingService.generateUseTaxAccount(cm); + for (final UseTaxContainer useTaxContainer : useTaxContainers) { + final List accounts = useTaxContainer.getAccounts(); + for (final SourceAccountingLine sourceAccountingLine : accounts) { + cm.generateGeneralLedgerPendingEntries(sourceAccountingLine, sequenceHelper, + useTaxContainer.getUseTax()); + sequenceHelper.increment(); + } + } + + // manually save cm account change tables (CAMS needs this) + if (!isCancel) { + purapAccountRevisionService.saveCreditMemoAccountRevisions(cm.getItems(), + cm.getPostingYearFromPendingGLEntries(), cm.getPostingPeriodCodeFromPendingGLEntries()); + } else { + purapAccountRevisionService.cancelCreditMemoAccountRevisions(cm.getItems(), + cm.getPostingYearFromPendingGLEntries(), cm.getPostingPeriodCodeFromPendingGLEntries()); + } + } + + saveGLEntries(cm.getGeneralLedgerPendingEntries()); + + LOG.debug("generateEntriesCreditMemo() ended"); + return true; + } + + @Override + public void generateEntriesApproveAmendPurchaseOrder(final PurchaseOrderDocument po) { + LOG.debug("generateEntriesApproveAmendPurchaseOrder() started"); + + // Set outstanding encumbered quantity/amount on items + for (final Object entry : po.getItems()) { + final PurchaseOrderItem item = (PurchaseOrderItem) entry; + + // if invoice fields are null (as would be for new items), set fields to zero + item.setItemInvoicedTotalAmount(item.getItemInvoicedTotalAmount() == null ? KualiDecimal.ZERO : + item.getItemInvoicedTotalAmount()); + item.setItemInvoicedTotalQuantity(item.getItemInvoicedTotalQuantity() == null ? KualiDecimal.ZERO : + item.getItemInvoicedTotalQuantity()); + + if (!item.isItemActiveIndicator()) { + // set outstanding encumbrance amounts to zero for inactive items + item.setItemOutstandingEncumberedQuantity(KualiDecimal.ZERO); + item.setItemOutstandingEncumberedAmount(KualiDecimal.ZERO); + + for (final PurApAccountingLine purApAccountingLine : item.getSourceAccountingLines()) { + final PurchaseOrderAccount account = (PurchaseOrderAccount) purApAccountingLine; + account.setItemAccountOutstandingEncumbranceAmount(KualiDecimal.ZERO); + account.setAlternateAmountForGLEntryCreation(KualiDecimal.ZERO); + } + } else { + // Set quantities + if (item.getItemQuantity() != null) { + item.setItemOutstandingEncumberedQuantity(item.getItemQuantity() + .subtract(item.getItemInvoicedTotalQuantity())); + } else { + // if order qty is null, outstanding encumbered qty should be null + item.setItemOutstandingEncumberedQuantity(null); + } + + // Set amount + if (item.getItemOutstandingEncumberedQuantity() != null) { + //do math as big decimal as doing it as a KualiDecimal will cause the item price to round to 2 + // digits + KualiDecimal itemEncumber = new KualiDecimal(item.getItemOutstandingEncumberedQuantity() + .bigDecimalValue().multiply(item.getItemUnitPrice())); + + //add tax for encumbrance + final KualiDecimal itemTaxAmount = item.getItemTaxAmount() == null ? KualiDecimal.ZERO : + item.getItemTaxAmount(); + itemEncumber = itemEncumber.add(itemTaxAmount); + + item.setItemOutstandingEncumberedAmount(itemEncumber); + } else { + if (item.getItemUnitPrice() != null) { + item.setItemOutstandingEncumberedAmount(new KualiDecimal(item.getItemUnitPrice() + .subtract(item.getItemInvoicedTotalAmount().bigDecimalValue()))); + } + } + + for (final PurApAccountingLine purApAccountingLine : item.getSourceAccountingLines()) { + final PurchaseOrderAccount account = (PurchaseOrderAccount) purApAccountingLine; + final KualiDecimal itemOutstandingEncumbranceAmt = calculateOutstandingEncumbranceAmt(item, account); + account.setItemAccountOutstandingEncumbranceAmount(itemOutstandingEncumbranceAmt); + account.setAlternateAmountForGLEntryCreation(itemOutstandingEncumbranceAmt); + } + } + } + + final PurchaseOrderDocument oldPO = purchaseOrderService.getCurrentPurchaseOrder(po.getPurapDocumentIdentifier()); + + if (oldPO == null) { + throw new IllegalArgumentException("Current Purchase Order not found - poId = " + + oldPO.getPurapDocumentIdentifier()); + } + + final List newAccounts = purapAccountingService + .generateSummaryWithNoZeroTotalsUsingAlternateAmount(po.getItemsActiveOnly()); + final List oldAccounts = purapAccountingService + .generateSummaryWithNoZeroTotalsUsingAlternateAmount(oldPO.getItemsActiveOnlySetupAlternateAmount()); + + final Map combination = new HashMap<>(); + + // Add amounts from the new PO + for (final SourceAccountingLine newAccount : newAccounts) { + combination.put(newAccount, newAccount.getAmount()); + } + + LOG.info("generateEntriesApproveAmendPurchaseOrder() combination after the add"); + for (final Object combinationKey : combination.keySet()) { + final SourceAccountingLine element = (SourceAccountingLine) combinationKey; + LOG.info( + "generateEntriesApproveAmendPurchaseOrder() {} = {}", + () -> element, + () -> combination.get(element).floatValue() + ); + } + + // Subtract the amounts from the old PO + for (final SourceAccountingLine oldAccount : oldAccounts) { + if (combination.containsKey(oldAccount)) { + KualiDecimal amount = combination.get(oldAccount); + amount = amount.subtract(oldAccount.getAmount()); + combination.put(oldAccount, amount); + } else { + combination.put(oldAccount, KualiDecimal.ZERO.subtract(oldAccount.getAmount())); + } + } + + LOG.debug("generateEntriesApproveAmendPurchaseOrder() combination after the subtract"); + for (final Object combinationKey : combination.keySet()) { + final SourceAccountingLine element = (SourceAccountingLine) combinationKey; + LOG.info( + "generateEntriesApproveAmendPurchaseOrder() {} = {}", + () -> element, + () -> combination.get(element).floatValue() + ); + } + + final List encumbranceAccounts = new ArrayList<>(); + for (final Object combinationKey : combination.keySet()) { + final SourceAccountingLine account = (SourceAccountingLine) combinationKey; + final KualiDecimal amount = combination.get(account); + if (KualiDecimal.ZERO.compareTo(amount) != 0) { + account.setAmount(amount); + encumbranceAccounts.add(account); + } + } + + po.setGlOnlySourceAccountingLines(encumbranceAccounts); + generalLedgerPendingEntryService.generateGeneralLedgerPendingEntries(po); + saveGLEntries(po.getGeneralLedgerPendingEntries()); + LOG.debug("generateEntriesApproveAmendPo() gl entries created; exit method"); + } + + // scope loosened for test purposes + KualiDecimal calculateOutstandingEncumbranceAmt(final PurchaseOrderItem item, final PurchaseOrderAccount account) { + BigDecimal percent = new BigDecimal(account.getAccountLinePercent().toString()); + percent = percent.divide(new BigDecimal("100")); + final BigDecimal itemOutstandingEncumbranceAmt = item.getItemOutstandingEncumberedAmount() + .bigDecimalValue().multiply(percent); + return new KualiDecimal(itemOutstandingEncumbranceAmt); + } + + @Override + public void generateEntriesClosePurchaseOrder(final PurchaseOrderDocument po) { + LOG.debug("generateEntriesClosePurchaseOrder() started"); + + // Set outstanding encumbered quantity/amount on items + for (final Object poItem : po.getItems()) { + final PurchaseOrderItem item = (PurchaseOrderItem) poItem; + final String logItmNbr = "Item # " + item.getItemLineNumber(); + + if (!item.isItemActiveIndicator()) { + continue; + } + + final KualiDecimal itemAmount; + LOG.debug("generateEntriesClosePurchaseOrder() {} Calculate based on amounts", logItmNbr); + + itemAmount = item.getItemOutstandingEncumberedAmount() == null ? KualiDecimal.ZERO : + item.getItemOutstandingEncumberedAmount(); + + KualiDecimal accountTotal = KualiDecimal.ZERO; + PurchaseOrderAccount lastAccount = null; + if (itemAmount.compareTo(KualiDecimal.ZERO) != 0) { + Collections.sort((List) item.getSourceAccountingLines()); + + for (final PurApAccountingLine purApAccountingLine : item.getSourceAccountingLines()) { + final PurchaseOrderAccount acct = (PurchaseOrderAccount) purApAccountingLine; + if (!acct.isEmpty()) { + final KualiDecimal acctAmount = itemAmount + .multiply(new KualiDecimal(acct.getAccountLinePercent().toString())) + .divide(PurapConstants.HUNDRED); + accountTotal = accountTotal.add(acctAmount); + acct.setAlternateAmountForGLEntryCreation(acctAmount); + lastAccount = acct; + } + } + + // account for rounding by adjusting last account as needed + if (lastAccount != null) { + final KualiDecimal difference = itemAmount.subtract(accountTotal); + LOG.debug("generateEntriesClosePurchaseOrder() difference: {} {}", logItmNbr, difference); + + final KualiDecimal amount = lastAccount.getAlternateAmountForGLEntryCreation(); + if (ObjectUtils.isNotNull(amount)) { + lastAccount.setAlternateAmountForGLEntryCreation(amount.add(difference)); + } else { + lastAccount.setAlternateAmountForGLEntryCreation(difference); + } + } + } + } + + po.setGlOnlySourceAccountingLines(purapAccountingService.generateSummaryWithNoZeroTotalsUsingAlternateAmount( + po.getItemsActiveOnly())); + if (shouldGenerateGLPEForPurchaseOrder(po)) { + generalLedgerPendingEntryService.generateGeneralLedgerPendingEntries(po); + saveGLEntries(po.getGeneralLedgerPendingEntries()); + LOG.debug("generateEntriesClosePurchaseOrder() gl entries created; exit method"); + } + + // Set outstanding encumbered quantity/amount on items + for (final Object poItem : po.getItems()) { + final PurchaseOrderItem item = (PurchaseOrderItem) poItem; + if (item.getItemType().isQuantityBasedGeneralLedgerIndicator()) { + item.setItemOutstandingEncumberedQuantity(KualiDecimal.ZERO); + } + item.setItemOutstandingEncumberedAmount(KualiDecimal.ZERO); + final List sourceAccountingLines = item.getSourceAccountingLines(); + for (final PurApAccountingLine purApAccountingLine : sourceAccountingLines) { + final PurchaseOrderAccount account = (PurchaseOrderAccount) purApAccountingLine; + account.setItemAccountOutstandingEncumbranceAmount(KualiDecimal.ZERO); + } + } + } + + /** + * We should not generate general ledger pending entries for Purchase Order Close Document and Purchase Order + * Reopen Document with $0 amount. + * + * @param po + * @return + */ + protected boolean shouldGenerateGLPEForPurchaseOrder(final PurchaseOrderDocument po) { + for (final SourceAccountingLine acct : po.getGlOnlySourceAccountingLines()) { + if (acct.getAmount().compareTo(KualiDecimal.ZERO) != 0) { + return true; + } + } + return false; + } + + @Override + public void generateEntriesReopenPurchaseOrder(final PurchaseOrderDocument po) { + LOG.debug("generateEntriesReopenPurchaseOrder() started"); + + // Set outstanding encumbered quantity/amount on items + for (final Object poItem : po.getItems()) { + final PurchaseOrderItem item = (PurchaseOrderItem) poItem; + if (item.getItemType().isQuantityBasedGeneralLedgerIndicator()) { + item.getItemQuantity().subtract(item.getItemInvoicedTotalQuantity()); + item.setItemOutstandingEncumberedQuantity( + item.getItemQuantity().subtract(item.getItemInvoicedTotalQuantity())); + item.setItemOutstandingEncumberedAmount(new KualiDecimal( + item.getItemOutstandingEncumberedQuantity().bigDecimalValue() + .multiply(item.getItemUnitPrice()))); + } else { + item.setItemOutstandingEncumberedAmount( + item.getTotalAmount().subtract(item.getItemInvoicedTotalAmount())); + } + final List sourceAccountingLines = item.getSourceAccountingLines(); + for (final PurApAccountingLine purApAccountingLine : sourceAccountingLines) { + final PurchaseOrderAccount account = (PurchaseOrderAccount) purApAccountingLine; + account.setItemAccountOutstandingEncumbranceAmount(new KualiDecimal( + item.getItemOutstandingEncumberedAmount().bigDecimalValue() + .multiply(account.getAccountLinePercent()) + .divide(KFSConstants.ONE_HUNDRED.bigDecimalValue()))); + } + } + + // Set outstanding encumbered quantity/amount on items + for (final Object poItem : po.getItems()) { + final PurchaseOrderItem item = (PurchaseOrderItem) poItem; + final String logItmNbr = "Item # " + item.getItemLineNumber(); + + if (!item.isItemActiveIndicator()) { + continue; + } + + final KualiDecimal itemAmount; + if (item.getItemType().isAmountBasedGeneralLedgerIndicator()) { + LOG.debug("generateEntriesReopenPurchaseOrder() {} Calculate based on amounts", logItmNbr); + itemAmount = item.getItemOutstandingEncumberedAmount() == null ? KualiDecimal.ZERO : + item.getItemOutstandingEncumberedAmount(); + } else { + LOG.debug("generateEntriesReopenPurchaseOrder() {} Calculate based on quantities", logItmNbr); + //do math as big decimal as doing it as a KualiDecimal will cause the item price to round to 2 digits + itemAmount = new KualiDecimal(item.getItemOutstandingEncumberedQuantity().bigDecimalValue() + .multiply(item.getItemUnitPrice())); + } + + KualiDecimal accountTotal = KualiDecimal.ZERO; + PurchaseOrderAccount lastAccount = null; + if (itemAmount.compareTo(KualiDecimal.ZERO) != 0) { + Collections.sort((List) item.getSourceAccountingLines()); + + for (final PurApAccountingLine purApAccountingLine : item.getSourceAccountingLines()) { + final PurchaseOrderAccount acct = (PurchaseOrderAccount) purApAccountingLine; + if (!acct.isEmpty()) { + final KualiDecimal acctAmount = itemAmount + .multiply(new KualiDecimal(acct.getAccountLinePercent().toString())) + .divide(PurapConstants.HUNDRED); + accountTotal = accountTotal.add(acctAmount); + acct.setAlternateAmountForGLEntryCreation(acctAmount); + lastAccount = acct; + } + } + + // account for rounding by adjusting last account as needed + if (lastAccount != null) { + final KualiDecimal difference = itemAmount.subtract(accountTotal); + LOG.debug("generateEntriesReopenPurchaseOrder() difference: {} {}", logItmNbr, difference); + + final KualiDecimal amount = lastAccount.getAlternateAmountForGLEntryCreation(); + if (ObjectUtils.isNotNull(amount)) { + lastAccount.setAlternateAmountForGLEntryCreation(amount.add(difference)); + } else { + lastAccount.setAlternateAmountForGLEntryCreation(difference); + } + } + } + } + + po.setGlOnlySourceAccountingLines(purapAccountingService.generateSummaryWithNoZeroTotalsUsingAlternateAmount( + po.getItemsActiveOnly())); + if (shouldGenerateGLPEForPurchaseOrder(po)) { + generalLedgerPendingEntryService.generateGeneralLedgerPendingEntries(po); + saveGLEntries(po.getGeneralLedgerPendingEntries()); + LOG.debug("generateEntriesReopenPurchaseOrder() gl entries created; exit method"); + } + LOG.debug("generateEntriesReopenPurchaseOrder() no gl entries created because the amount is 0; exit method"); + } + + @Override + public void generateEntriesVoidPurchaseOrder(final PurchaseOrderDocument po) { + LOG.debug("generateEntriesVoidPurchaseOrder() started"); + + // Set outstanding encumbered quantity/amount on items + for (final Object poItem : po.getItems()) { + final PurchaseOrderItem item = (PurchaseOrderItem) poItem; + final String logItmNbr = "Item # " + item.getItemLineNumber(); + + if (!item.isItemActiveIndicator()) { + continue; + } + + // just use the outstanding amount as recalculating here, particularly the item tax will cause amounts to + // be over or under encumbered and the remaining encumbered amount should be unencumbered during a close + LOG.debug("generateEntriesVoidPurchaseOrder() {} Calculate based on amounts", logItmNbr); + + final KualiDecimal itemAmount = item.getItemOutstandingEncumberedAmount() == null ? KualiDecimal.ZERO : + item.getItemOutstandingEncumberedAmount(); + + KualiDecimal accountTotal = KualiDecimal.ZERO; + PurchaseOrderAccount lastAccount = null; + if (itemAmount.compareTo(KualiDecimal.ZERO) != 0) { + Collections.sort((List) item.getSourceAccountingLines()); + + for (final PurApAccountingLine purApAccountingLine : item.getSourceAccountingLines()) { + final PurchaseOrderAccount acct = (PurchaseOrderAccount) purApAccountingLine; + if (!acct.isEmpty()) { + final KualiDecimal acctAmount = itemAmount + .multiply(new KualiDecimal(acct.getAccountLinePercent().toString())) + .divide(PurapConstants.HUNDRED); + accountTotal = accountTotal.add(acctAmount); + acct.setAlternateAmountForGLEntryCreation(acctAmount); + lastAccount = acct; + } + } + + // account for rounding by adjusting last account as needed + if (lastAccount != null) { + final KualiDecimal difference = itemAmount.subtract(accountTotal); + LOG.debug("generateEntriesVoidPurchaseOrder() difference: {} {}", logItmNbr, difference); + + final KualiDecimal amount = lastAccount.getAlternateAmountForGLEntryCreation(); + if (ObjectUtils.isNotNull(amount)) { + lastAccount.setAlternateAmountForGLEntryCreation(amount.add(difference)); + } else { + lastAccount.setAlternateAmountForGLEntryCreation(difference); + } + } + } + } + + po.setGlOnlySourceAccountingLines(purapAccountingService.generateSummaryWithNoZeroTotalsUsingAlternateAmount( + po.getItemsActiveOnly())); + generalLedgerPendingEntryService.generateGeneralLedgerPendingEntries(po); + saveGLEntries(po.getGeneralLedgerPendingEntries()); + LOG.debug("generateEntriesVoidPurchaseOrder() gl entries created; exit method"); + } + + /** + * Relieve the Encumbrance on a PO based on values in a PREQ. This is to be called when a PREQ is created. Note: + * This modifies the encumbrance values on the PO and saves the PO + * + * @param preq PREQ for invoice + * @return List of accounting lines to use to create the pending general ledger entries + */ + protected List relieveEncumbrance(final PaymentRequestDocument preq) { + LOG.debug("relieveEncumbrance() started"); + + final Map encumbranceAccountMap = new HashMap<>(); + final PurchaseOrderDocument po = purchaseOrderService.getCurrentPurchaseOrder(preq.getPurchaseOrderIdentifier()); + + for (final Object item : preq.getItems()) { + final PaymentRequestItem preqItem = (PaymentRequestItem) item; + final PurchaseOrderItem poItem = getPoItem(po, preqItem.getItemLineNumber(), preqItem.getItemType()); + + // Set this true if we relieve the entire encumbrance + boolean takeAll = false; + // Amount to disencumber for this item + KualiDecimal itemDisEncumber; + + final String logItmNbr = "Item # " + preqItem.getItemLineNumber(); + LOG.debug("relieveEncumbrance() {}", logItmNbr); + + // If there isn't a PO item or the extended price is 0, we don't need encumbrances + if (poItem == null) { + LOG.debug("relieveEncumbrance() {} No encumbrances required because po item is null", logItmNbr); + } else { + KualiDecimal preqItemTotalAmount = preqItem.getTotalAmount(); + if (preqItemTotalAmount == null) { + preqItemTotalAmount = KualiDecimal.ZERO; + } + if (KualiDecimal.ZERO.compareTo(preqItemTotalAmount) == 0) { + /* + * This is a specialized case where PREQ item being processed must adjust the PO item's + * outstanding encumbered quantity. This kind of scenario is mostly seen on warranty type items. + * The following must be true to do this: PREQ item Extended Price must be ZERO, PREQ item + * invoice quantity must be not empty and not ZERO, and PO item is quantity based PO item unit + * cost is ZERO + */ + LOG.debug( + "relieveEncumbrance() {} No GL encumbrances required because extended price is ZERO", + logItmNbr + ); + + if (poItem.getItemQuantity() != null + && BigDecimal.ZERO.compareTo(poItem.getItemUnitPrice()) == 0) { + // po has order quantity and unit price is ZERO... reduce outstanding encumbered quantity + LOG.debug("relieveEncumbrance() {} Calculate po outstanding encumbrance", logItmNbr); + + // Do encumbrance calculations based on quantity + if (preqItem.getItemQuantity() != null + && KualiDecimal.ZERO.compareTo(preqItem.getItemQuantity()) != 0) { + final KualiDecimal invoiceQuantity = preqItem.getItemQuantity(); + final KualiDecimal outstandingEncumberedQuantity = + poItem.getItemOutstandingEncumberedQuantity() == null ? KualiDecimal.ZERO : + poItem.getItemOutstandingEncumberedQuantity(); + + final KualiDecimal encumbranceQuantity; + if (invoiceQuantity.compareTo(outstandingEncumberedQuantity) > 0) { + // We bought more than the quantity on the PO + LOG.debug("relieveEncumbrance() {} we bought more than the qty on the PO", logItmNbr); + poItem.setItemOutstandingEncumberedQuantity(KualiDecimal.ZERO); + } else { + encumbranceQuantity = invoiceQuantity; + poItem.setItemOutstandingEncumberedQuantity( + outstandingEncumberedQuantity.subtract(encumbranceQuantity)); + LOG.debug( + "relieveEncumbrance() {} adjusting outstanding encumbrance qty - " + + "encumbranceQty {} outstandingEncumberedQty {}", + () -> logItmNbr, + () -> encumbranceQuantity, + poItem::getItemOutstandingEncumberedQuantity + ); + } + + if (poItem.getItemInvoicedTotalQuantity() == null) { + poItem.setItemInvoicedTotalQuantity(invoiceQuantity); + } else { + poItem.setItemInvoicedTotalQuantity( + poItem.getItemInvoicedTotalQuantity().add(invoiceQuantity)); + } + } + } + } else { + LOG.debug("relieveEncumbrance() {} Calculate encumbrance GL entries", logItmNbr); + + // Do we calculate the encumbrance amount based on quantity or amount? + if (poItem.getItemType().isQuantityBasedGeneralLedgerIndicator()) { + LOG.debug("relieveEncumbrance() {} Calculate encumbrance based on quantity", logItmNbr); + + // Do encumbrance calculations based on quantity + final KualiDecimal invoiceQuantity = + preqItem.getItemQuantity() == null ? KualiDecimal.ZERO : preqItem.getItemQuantity(); + final KualiDecimal outstandingEncumberedQuantity = + poItem.getItemOutstandingEncumberedQuantity() == null ? KualiDecimal.ZERO : + poItem.getItemOutstandingEncumberedQuantity(); + + final KualiDecimal encumbranceQuantity; + + if (invoiceQuantity.compareTo(outstandingEncumberedQuantity) > 0) { + // We bought more than the quantity on the PO + LOG.debug("relieveEncumbrance() {} we bought more than the qty on the PO", logItmNbr); + + encumbranceQuantity = outstandingEncumberedQuantity; + poItem.setItemOutstandingEncumberedQuantity(KualiDecimal.ZERO); + takeAll = true; + } else { + encumbranceQuantity = invoiceQuantity; + poItem.setItemOutstandingEncumberedQuantity( + outstandingEncumberedQuantity.subtract(encumbranceQuantity)); + if (KualiDecimal.ZERO.compareTo(poItem.getItemOutstandingEncumberedQuantity()) == 0) { + takeAll = true; + } + LOG.debug( + "relieveEncumbrance() {} encumbranceQty {} outstandingEncumberedQty {}", + () -> logItmNbr, + () -> encumbranceQuantity, + poItem::getItemOutstandingEncumberedQuantity + ); + } + + if (poItem.getItemInvoicedTotalQuantity() == null) { + poItem.setItemInvoicedTotalQuantity(invoiceQuantity); + } else { + poItem.setItemInvoicedTotalQuantity( + poItem.getItemInvoicedTotalQuantity().add(invoiceQuantity)); + } + + itemDisEncumber = new KualiDecimal(encumbranceQuantity.bigDecimalValue() + .multiply(poItem.getItemUnitPrice())); + + //add tax for encumbrance + final KualiDecimal itemTaxAmount = poItem.getItemTaxAmount() == null ? KualiDecimal.ZERO : + poItem.getItemTaxAmount(); + final KualiDecimal encumbranceTaxAmount = + encumbranceQuantity.divide(poItem.getItemQuantity()).multiply(itemTaxAmount); + itemDisEncumber = itemDisEncumber.add(encumbranceTaxAmount); + } else { + LOG.debug("relieveEncumbrance() {} Calculate encumbrance based on amount", logItmNbr); + + // Do encumbrance calculations based on amount only + if (poItem.getItemOutstandingEncumberedAmount().bigDecimalValue().signum() == -1 + && preqItemTotalAmount.bigDecimalValue().signum() == -1) { + LOG.debug( + "relieveEncumbrance() {} Outstanding Encumbered amount is negative: {}", + () -> logItmNbr, + poItem::getItemOutstandingEncumberedAmount + ); + + if (preqItemTotalAmount.compareTo(poItem.getItemOutstandingEncumberedAmount()) >= 0) { + // extended price is equal to or greater than outstanding encumbered + itemDisEncumber = preqItemTotalAmount; + } else { + // extended price is less than outstanding encumbered + takeAll = true; + itemDisEncumber = poItem.getItemOutstandingEncumberedAmount(); + } + } else { + LOG.debug( + "relieveEncumbrance() {} Outstanding Encumbered amount is positive or ZERO: {}", + () -> logItmNbr, + poItem::getItemOutstandingEncumberedAmount + ); + + if (poItem.getItemOutstandingEncumberedAmount().compareTo(preqItemTotalAmount) >= 0) { + // outstanding amount is equal to or greater than extended price + itemDisEncumber = preqItemTotalAmount; + } else { + // outstanding amount is less than extended price + takeAll = true; + itemDisEncumber = poItem.getItemOutstandingEncumberedAmount(); + } + } + } + + LOG.debug("relieveEncumbrance() {} Amount to disencumber: {}", logItmNbr, itemDisEncumber); + + final KualiDecimal newOutstandingEncumberedAmount = poItem.getItemOutstandingEncumberedAmount() + .subtract(itemDisEncumber); + LOG.debug( + "relieveEncumbrance() {} New Outstanding Encumbered amount is : {}", + logItmNbr, + newOutstandingEncumberedAmount + ); + + poItem.setItemOutstandingEncumberedAmount(newOutstandingEncumberedAmount); + + final KualiDecimal newInvoicedTotalAmount = poItem.getItemInvoicedTotalAmount().add(preqItemTotalAmount); + LOG.debug( + "relieveEncumbrance() {} New Invoiced Total Amount is: {}", + logItmNbr, + newInvoicedTotalAmount + ); + + poItem.setItemInvoicedTotalAmount(newInvoicedTotalAmount); + + Collections.sort((List) poItem.getSourceAccountingLines()); + + // make the list of accounts for the disencumbrance entry + PurchaseOrderAccount lastAccount = null; + KualiDecimal accountTotal = KualiDecimal.ZERO; + for (final PurApAccountingLine purApAccountingLine : poItem.getSourceAccountingLines()) { + final PurchaseOrderAccount account = (PurchaseOrderAccount) purApAccountingLine; + if (!account.isEmpty()) { + final KualiDecimal encumbranceAmount; + final SourceAccountingLine acctString = account.generateSourceAccountingLine(); + if (takeAll) { + // fully paid; remove remaining encumbrance + encumbranceAmount = account.getItemAccountOutstandingEncumbranceAmount(); + account.setItemAccountOutstandingEncumbranceAmount(KualiDecimal.ZERO); + LOG.debug("relieveEncumbrance() {} take all", logItmNbr); + } else { + // amount = item disencumber * account percent / 100 + encumbranceAmount = itemDisEncumber + .multiply(new KualiDecimal(account.getAccountLinePercent().toString())) + .divide(PurapConstants.HUNDRED); + + account.setItemAccountOutstandingEncumbranceAmount( + account.getItemAccountOutstandingEncumbranceAmount() + .subtract(encumbranceAmount)); + + // For rounding check at the end + accountTotal = accountTotal.add(encumbranceAmount); + + // If we are zeroing out the encumbrance, we don't need to adjust for rounding + if (!takeAll) { + lastAccount = account; + } + } + + LOG.debug("relieveEncumbrance() {} {} = {}", logItmNbr, acctString, encumbranceAmount); + + if (ObjectUtils.isNull(encumbranceAccountMap.get(acctString))) { + encumbranceAccountMap.put(acctString, encumbranceAmount); + } else { + final KualiDecimal amt = encumbranceAccountMap.get(acctString); + encumbranceAccountMap.put(acctString, amt.add(encumbranceAmount)); + } + } + } + + // account for rounding by adjusting last account as needed + if (lastAccount != null) { + final KualiDecimal difference = itemDisEncumber.subtract(accountTotal); + LOG.debug("relieveEncumbrance() difference: {} {}", logItmNbr, difference); + + final SourceAccountingLine acctString = lastAccount.generateSourceAccountingLine(); + final KualiDecimal amount = encumbranceAccountMap.get(acctString); + if (ObjectUtils.isNull(amount)) { + encumbranceAccountMap.put(acctString, difference); + } else { + encumbranceAccountMap.put(acctString, amount.add(difference)); + } + + lastAccount.setItemAccountOutstandingEncumbranceAmount( + lastAccount.getItemAccountOutstandingEncumbranceAmount().subtract(difference)); + } + } + } + } + + final List encumbranceAccounts = new ArrayList<>(); + for (final SourceAccountingLine acctString : encumbranceAccountMap.keySet()) { + final KualiDecimal amount = encumbranceAccountMap.get(acctString); + if (amount.doubleValue() != 0) { + acctString.setAmount(amount); + encumbranceAccounts.add(acctString); + } + } + + businessObjectService.save(po); + return encumbranceAccounts; + } + + /** + * Re-encumber the Encumbrance on a PO based on values in a PREQ. This is used when a PREQ is cancelled. + * Note: This modifies the encumbrance values on the PO and saves the PO + * + * @param preq PREQ for invoice + * @return List of accounting lines to use to create the pending general ledger entries + */ + protected List reencumberEncumbrance(final PaymentRequestDocument preq) { + LOG.debug("reencumberEncumbrance() started"); + + final PurchaseOrderDocument po = purchaseOrderService.getCurrentPurchaseOrder(preq.getPurchaseOrderIdentifier()); + final Map encumbranceAccountMap = new HashMap<>(); + + for (final Object item : preq.getItems()) { + final PaymentRequestItem payRequestItem = (PaymentRequestItem) item; + final PurchaseOrderItem poItem = getPoItem(po, payRequestItem.getItemLineNumber(), payRequestItem.getItemType()); + + // Amount to reencumber for this item + KualiDecimal itemReEncumber; + + final String logItmNbr = "Item # " + payRequestItem.getItemLineNumber(); + LOG.debug("reencumberEncumbrance() {}", logItmNbr); + + // If there isn't a PO item or the total amount is 0, we don't need encumbrances + final KualiDecimal preqItemTotalAmount = payRequestItem.getTotalAmount() == null ? KualiDecimal.ZERO : + payRequestItem.getTotalAmount(); + if (poItem == null || preqItemTotalAmount.doubleValue() == 0) { + LOG.debug("reencumberEncumbrance() {} No encumbrances required", logItmNbr); + } else { + LOG.debug("reencumberEncumbrance() {} Calculate encumbrance GL entries", logItmNbr); + + // Do we calculate the encumbrance amount based on quantity or amount? + if (poItem.getItemType().isQuantityBasedGeneralLedgerIndicator()) { + LOG.debug("reencumberEncumbrance() {} Calculate encumbrance based on quantity", logItmNbr); + + // Do disencumbrance calculations based on quantity + final KualiDecimal preqQuantity = payRequestItem.getItemQuantity() == null ? KualiDecimal.ZERO : + payRequestItem.getItemQuantity(); + final KualiDecimal outstandingEncumberedQuantity = + poItem.getItemOutstandingEncumberedQuantity() == null ? KualiDecimal.ZERO : + poItem.getItemOutstandingEncumberedQuantity(); + final KualiDecimal invoicedTotal = poItem.getItemInvoicedTotalQuantity() == null ? KualiDecimal.ZERO : + poItem.getItemInvoicedTotalQuantity(); + + poItem.setItemInvoicedTotalQuantity(invoicedTotal.subtract(preqQuantity)); + poItem.setItemOutstandingEncumberedQuantity(outstandingEncumberedQuantity.add(preqQuantity)); + + //do math as big decimal as doing it as a KualiDecimal will cause the item price to round to 2 + // digits + itemReEncumber = new KualiDecimal(preqQuantity.bigDecimalValue() + .multiply(poItem.getItemUnitPrice())); + + //add tax for encumbrance + final KualiDecimal itemTaxAmount = poItem.getItemTaxAmount() == null ? KualiDecimal.ZERO : + poItem.getItemTaxAmount(); + final KualiDecimal encumbranceTaxAmount = preqQuantity.divide(poItem.getItemQuantity()) + .multiply(itemTaxAmount); + itemReEncumber = itemReEncumber.add(encumbranceTaxAmount); + } else { + LOG.debug("reencumberEncumbrance() {} Calculate encumbrance based on amount", logItmNbr); + + itemReEncumber = preqItemTotalAmount; + // if re-encumber amount is more than original PO ordered amount... do not exceed ordered amount + // this prevents negative encumbrance + if (poItem.getTotalAmount() != null && poItem.getTotalAmount().bigDecimalValue().signum() < 0) { + // po item extended cost is negative + if (poItem.getTotalAmount().compareTo(itemReEncumber) > 0) { + itemReEncumber = poItem.getTotalAmount(); + } + } else if (poItem.getTotalAmount() != null + && poItem.getTotalAmount().bigDecimalValue().signum() >= 0) { + // po item extended cost is positive + if (poItem.getTotalAmount().compareTo(itemReEncumber) < 0) { + itemReEncumber = poItem.getTotalAmount(); + } + } + } + + LOG.debug("reencumberEncumbrance() {} Amount to reencumber: {}", logItmNbr, itemReEncumber); + + final KualiDecimal outstandingEncumberedAmount = poItem.getItemOutstandingEncumberedAmount() == null ? + KualiDecimal.ZERO : poItem.getItemOutstandingEncumberedAmount(); + LOG.debug( + "reencumberEncumbrance() {} PO Item Outstanding Encumbrance Amount set to: {}", + logItmNbr, + outstandingEncumberedAmount + ); + final KualiDecimal newOutstandingEncumberedAmount = outstandingEncumberedAmount.add(itemReEncumber); + LOG.debug( + "reencumberEncumbrance() {} New PO Item Outstanding Encumbrance Amount to set: {}", + logItmNbr, + newOutstandingEncumberedAmount + ); + + poItem.setItemOutstandingEncumberedAmount(newOutstandingEncumberedAmount); + + final KualiDecimal invoicedTotalAmount = poItem.getItemInvoicedTotalAmount() == null ? KualiDecimal.ZERO : + poItem.getItemInvoicedTotalAmount(); + LOG.debug( + "reencumberEncumbrance() {} PO Item Invoiced Total Amount set to: {}", + logItmNbr, + invoicedTotalAmount + ); + + final KualiDecimal newInvoicedTotalAmount = invoicedTotalAmount.subtract(preqItemTotalAmount); + LOG.debug( + "reencumberEncumbrance() {} New PO Item Invoiced Total Amount to set: {}", + logItmNbr, + newInvoicedTotalAmount + ); + + poItem.setItemInvoicedTotalAmount(newInvoicedTotalAmount); + + // make the list of accounts for the reencumbrance entry + PurchaseOrderAccount lastAccount = null; + KualiDecimal accountTotal = KualiDecimal.ZERO; + + Collections.sort((List) poItem.getSourceAccountingLines()); + + for (final PurApAccountingLine purApAccountingLine : poItem.getSourceAccountingLines()) { + final PurchaseOrderAccount account = (PurchaseOrderAccount) purApAccountingLine; + if (!account.isEmpty()) { + final SourceAccountingLine acctString = account.generateSourceAccountingLine(); + + // amount = item reencumber * account percent / 100 + final KualiDecimal reencumbranceAmount = itemReEncumber + .multiply(new KualiDecimal(account.getAccountLinePercent().toString())) + .divide(PurapConstants.HUNDRED); + + account.setItemAccountOutstandingEncumbranceAmount( + account.getItemAccountOutstandingEncumbranceAmount().add(reencumbranceAmount)); + + // For rounding check at the end + accountTotal = accountTotal.add(reencumbranceAmount); + + lastAccount = account; + + LOG.debug("reencumberEncumbrance() {} {} = {}", logItmNbr, acctString, reencumbranceAmount); + + if (encumbranceAccountMap.containsKey(acctString)) { + final KualiDecimal currentAmount = encumbranceAccountMap.get(acctString); + encumbranceAccountMap.put(acctString, reencumbranceAmount.add(currentAmount)); + } else { + encumbranceAccountMap.put(acctString, reencumbranceAmount); + } + } + } + + // account for rounding by adjusting last account as needed + if (lastAccount != null) { + final KualiDecimal difference = itemReEncumber.subtract(accountTotal); + LOG.debug("reencumberEncumbrance() difference: {} {}", logItmNbr, difference); + + final SourceAccountingLine acctString = lastAccount.generateSourceAccountingLine(); + final KualiDecimal amount = encumbranceAccountMap.get(acctString); + if (amount == null) { + encumbranceAccountMap.put(acctString, difference); + } else { + encumbranceAccountMap.put(acctString, amount.add(difference)); + } + lastAccount.setItemAccountOutstandingEncumbranceAmount( + lastAccount.getItemAccountOutstandingEncumbranceAmount().add(difference)); + } + } + } + + businessObjectService.save(po); + + final List encumbranceAccounts = new ArrayList<>(); + for (final SourceAccountingLine acctString : encumbranceAccountMap.keySet()) { + final KualiDecimal amount = encumbranceAccountMap.get(acctString); + if (amount.doubleValue() != 0) { + acctString.setAmount(amount); + encumbranceAccounts.add(acctString); + } + } + + return encumbranceAccounts; + } + + /** + * Re-encumber the Encumbrance on a PO based on values in a PREQ. This is used when a PREQ is cancelled. + * Note: This modifies the encumbrance values on the PO and saves the PO + * + * @param cm Credit Memo document + * @param po Purchase Order document modify encumbrances + * @return List of accounting lines to use to create the pending general ledger entries + */ + protected List getCreditMemoEncumbrance( + final VendorCreditMemoDocument cm, + final PurchaseOrderDocument po, final boolean cancel) { + LOG.debug("getCreditMemoEncumbrance() started"); + + if (ObjectUtils.isNull(po)) { + return null; + } + + if (cancel) { + LOG.debug("getCreditMemoEncumbrance() Receiving items back from vendor (cancelled CM)"); + } else { + LOG.debug("getCreditMemoEncumbrance() Returning items to vendor"); + } + + final Map encumbranceAccountMap = new HashMap<>(); + + // Get each item one by one + for (final Object item : cm.getItems()) { + final CreditMemoItem cmItem = (CreditMemoItem) item; + final PurchaseOrderItem poItem = getPoItem(po, cmItem.getItemLineNumber(), cmItem.getItemType()); + + // Amount to disencumber for this item + KualiDecimal itemDisEncumber; + // Amount to alter the invoicedAmt on the PO item + KualiDecimal itemAlterInvoiceAmt; + + final String logItmNbr = "Item # " + cmItem.getItemLineNumber(); + LOG.debug("getCreditMemoEncumbrance() {}", logItmNbr); + + final KualiDecimal cmItemTotalAmount = + cmItem.getTotalAmount() == null ? KualiDecimal.ZERO : cmItem.getTotalAmount(); + // If there isn't a PO item or the total amount is 0, we don't need encumbrances + if (poItem == null || cmItemTotalAmount == null || cmItemTotalAmount.doubleValue() == 0) { + LOG.debug("getCreditMemoEncumbrance() {} No encumbrances required", logItmNbr); + } else { + LOG.debug("getCreditMemoEncumbrance() {} Calculate encumbrance GL entries", logItmNbr); + + // Do we calculate the encumbrance amount based on quantity or amount? + if (poItem.getItemType().isQuantityBasedGeneralLedgerIndicator()) { + LOG.debug("getCreditMemoEncumbrance() {} Calculate encumbrance based on quantity", logItmNbr); + + // Do encumbrance calculations based on quantity + final KualiDecimal cmQuantity = cmItem.getItemQuantity() == null ? KualiDecimal.ZERO : + cmItem.getItemQuantity(); + + final KualiDecimal encumbranceQuantityChange = calculateQuantityChange(cancel, poItem, cmQuantity); + + LOG.debug( + "getCreditMemoEncumbrance() {} encumbranceQtyChange {} outstandingEncumberedQty {} invoicedTotalQuantity {}", + () -> logItmNbr, + () -> encumbranceQuantityChange, + poItem::getItemOutstandingEncumberedQuantity, + poItem::getItemInvoicedTotalQuantity + ); + + //do math as big decimal as doing it as a KualiDecimal will cause the item price to round to 2 + // digits + itemDisEncumber = new KualiDecimal(encumbranceQuantityChange.bigDecimalValue() + .multiply(poItem.getItemUnitPrice())); + + //add tax for encumbrance + final KualiDecimal itemTaxAmount = poItem.getItemTaxAmount() == null ? KualiDecimal.ZERO : + poItem.getItemTaxAmount(); + final KualiDecimal encumbranceTaxAmount = encumbranceQuantityChange.divide(poItem.getItemQuantity()) + .multiply(itemTaxAmount); + itemDisEncumber = itemDisEncumber.add(encumbranceTaxAmount); + + itemAlterInvoiceAmt = cmItemTotalAmount; + if (cancel) { + itemAlterInvoiceAmt = itemAlterInvoiceAmt.multiply(new KualiDecimal("-1")); + } + } else { + LOG.debug("getCreditMemoEncumbrance() {} Calculate encumbrance based on amount", logItmNbr); + + // Do encumbrance calculations based on amount only + if (cancel) { + // Decrease encumbrance + itemDisEncumber = cmItemTotalAmount.multiply(new KualiDecimal("-1")); + + if (poItem.getItemOutstandingEncumberedAmount().add(itemDisEncumber).doubleValue() < 0) { + LOG.debug("getCreditMemoEncumbrance() Cancel overflow"); + itemDisEncumber = poItem.getItemOutstandingEncumberedAmount(); + } + } else { + // Increase encumbrance + itemDisEncumber = cmItemTotalAmount; + + if (poItem.getItemOutstandingEncumberedAmount().add(itemDisEncumber).doubleValue() > + poItem.getTotalAmount().doubleValue()) { + LOG.debug("getCreditMemoEncumbrance() Create overflow"); + + itemDisEncumber = poItem.getTotalAmount() + .subtract(poItem.getItemOutstandingEncumberedAmount()); + } + } + itemAlterInvoiceAmt = itemDisEncumber; + } + + // alter the encumbrance based on what was originally encumbered + poItem.setItemOutstandingEncumberedAmount(poItem.getItemOutstandingEncumberedAmount() + .add(itemDisEncumber)); + + // alter the invoiced amt based on what was actually credited on the credit memo + poItem.setItemInvoicedTotalAmount(poItem.getItemInvoicedTotalAmount().subtract(itemAlterInvoiceAmt)); + if (poItem.getItemInvoicedTotalAmount().compareTo(KualiDecimal.ZERO) < 0) { + poItem.setItemInvoicedTotalAmount(KualiDecimal.ZERO); + } + + LOG.debug("getCreditMemoEncumbrance() {} Amount to disencumber: {}", logItmNbr, itemDisEncumber); + + Collections.sort((List) poItem.getSourceAccountingLines()); + + // make the list of accounts for the disencumbrance entry + PurchaseOrderAccount lastAccount = null; + KualiDecimal accountTotal = KualiDecimal.ZERO; + + for (final PurApAccountingLine purApAccountingLine : poItem.getSourceAccountingLines()) { + final PurchaseOrderAccount account = (PurchaseOrderAccount) purApAccountingLine; + if (!account.isEmpty()) { + final KualiDecimal encumbranceAmount; + + final SourceAccountingLine acctString = account.generateSourceAccountingLine(); + // amount = item disencumber * account percent / 100 + encumbranceAmount = itemDisEncumber + .multiply(new KualiDecimal(account.getAccountLinePercent().toString())) + .divide(new KualiDecimal(100)); + + account.setItemAccountOutstandingEncumbranceAmount( + account.getItemAccountOutstandingEncumbranceAmount().add(encumbranceAmount)); + + // For rounding check at the end + accountTotal = accountTotal.add(encumbranceAmount); + + lastAccount = account; + + LOG.debug("getCreditMemoEncumbrance() {} {} = {}", logItmNbr, acctString, encumbranceAmount); + + if (encumbranceAccountMap.get(acctString) == null) { + encumbranceAccountMap.put(acctString, encumbranceAmount); + } else { + final KualiDecimal amt = encumbranceAccountMap.get(acctString); + encumbranceAccountMap.put(acctString, amt.add(encumbranceAmount)); + } + } + } + + // account for rounding by adjusting last account as needed + if (lastAccount != null) { + final KualiDecimal difference = itemDisEncumber.subtract(accountTotal); + LOG.debug("getCreditMemoEncumbrance() difference: {} {}", logItmNbr, difference); + + final SourceAccountingLine acctString = lastAccount.generateSourceAccountingLine(); + final KualiDecimal amount = encumbranceAccountMap.get(acctString); + if (amount == null) { + encumbranceAccountMap.put(acctString, difference); + } else { + encumbranceAccountMap.put(acctString, amount.add(difference)); + } + lastAccount.setItemAccountOutstandingEncumbranceAmount( + lastAccount.getItemAccountOutstandingEncumbranceAmount().add(difference)); + } + } + } + + final List encumbranceAccounts = new ArrayList<>(); + for (final SourceAccountingLine acctString : encumbranceAccountMap.keySet()) { + final KualiDecimal amount = encumbranceAccountMap.get(acctString); + if (amount.doubleValue() != 0) { + acctString.setAmount(amount); + encumbranceAccounts.add(acctString); + } + } + + businessObjectService.save(po); + return encumbranceAccounts; + } + + /** + * Save the given general ledger entries + * + * @param glEntries List of GeneralLedgerPendingEntries to be saved + */ + protected void saveGLEntries(final List glEntries) { + LOG.debug("saveGLEntries() started"); + businessObjectService.save(glEntries); + } + + /** + * Save the given accounts for the given document. + * + * @param summaryAccounts Accounts to be saved + * @param purapDocumentIdentifier Purap document id for accounts + */ + protected void saveAccountsPayableSummaryAccounts( + final List summaryAccounts, + final Integer purapDocumentIdentifier, final String docType) { + LOG.debug("saveAccountsPayableSummaryAccounts() started"); + purapAccountingService.deleteSummaryAccounts(purapDocumentIdentifier, docType); + final List apSummaryAccounts = new ArrayList<>(); + for (final SummaryAccount summaryAccount : summaryAccounts) { + apSummaryAccounts.add(new AccountsPayableSummaryAccount( + summaryAccount.getAccount(), + purapDocumentIdentifier, + docType, + dateTimeService.getCurrentTimestamp())); + } + businessObjectService.save(apSummaryAccounts); + } + + /** + * Find item in PO based on given parameters. Must send either the line # or item type. + * + * @param po Purchase Order containing list of items + * @param nbr Line # of desired item (could be null) + * @param itemType Item type of desired item + * @return PurchaseOrderItem found matching given criteria + */ + protected PurchaseOrderItem getPoItem(final PurchaseOrderDocument po, final Integer nbr, final ItemType itemType) { + LOG.debug("getPoItem() started"); + for (final Object item : po.getItems()) { + final PurchaseOrderItem element = (PurchaseOrderItem) item; + if (itemType.isLineItemIndicator()) { + if (ObjectUtils.isNotNull(nbr) && ObjectUtils.isNotNull(element.getItemLineNumber()) + && nbr.compareTo(element.getItemLineNumber()) == 0) { + return element; + } + } else { + if (element.getItemTypeCode().equals(itemType.getItemTypeCode())) { + return element; + } + } + } + return null; + } + + /** + * Format description for general ledger entry. Currently making sure length is less than 40 char. + * + * @param description String to be formatted + * @return Formatted String + */ + protected String entryDescription(final String description) { + if (description != null && description.length() > 40) { + return description.substring(0, 39); + } else { + return description; + } + } + + /** + * Calculate quantity change for creating Credit Memo entries + * + * @param cancel Boolean indicating whether entries are for creation or cancellation of credit memo + * @param poItem Purchase Order Item + * @param cmQuantity Quantity on credit memo item + * @return Calculated change + */ + protected KualiDecimal calculateQuantityChange(final boolean cancel, final PurchaseOrderItem poItem, final KualiDecimal cmQuantity) { + LOG.debug("calculateQuantityChange() started"); + + // Calculate quantity change & adjust invoiced quantity & outstanding encumbered quantity + KualiDecimal encumbranceQuantityChange; + if (cancel) { + encumbranceQuantityChange = cmQuantity.multiply(new KualiDecimal("-1")); + } else { + encumbranceQuantityChange = cmQuantity; + } + poItem.setItemInvoicedTotalQuantity(poItem.getItemInvoicedTotalQuantity() + .subtract(encumbranceQuantityChange)); + poItem.setItemOutstandingEncumberedQuantity(poItem.getItemOutstandingEncumberedQuantity() + .add(encumbranceQuantityChange)); + + // Check for overflows + if (cancel) { + if (poItem.getItemOutstandingEncumberedQuantity().doubleValue() < 0) { + LOG.debug("calculateQuantityChange() Cancel overflow"); + final KualiDecimal difference = poItem.getItemOutstandingEncumberedQuantity().abs(); + poItem.setItemOutstandingEncumberedQuantity(KualiDecimal.ZERO); + poItem.setItemInvoicedTotalQuantity(poItem.getItemQuantity()); + encumbranceQuantityChange = encumbranceQuantityChange.add(difference); + } + } else { + if (poItem.getItemInvoicedTotalQuantity().doubleValue() < 0) { + LOG.debug("calculateQuantityChange() Create overflow"); + final KualiDecimal difference = poItem.getItemInvoicedTotalQuantity().abs(); + poItem.setItemOutstandingEncumberedQuantity(poItem.getItemQuantity()); + poItem.setItemInvoicedTotalQuantity(KualiDecimal.ZERO); + encumbranceQuantityChange = encumbranceQuantityChange.add(difference); + } + } + return encumbranceQuantityChange; + } + + public void setBusinessObjectService(final BusinessObjectService businessObjectService) { + this.businessObjectService = businessObjectService; + } + + public void setDateTimeService(final DateTimeService dateTimeService) { + this.dateTimeService = dateTimeService; + } + + public void setGeneralLedgerPendingEntryService(final GeneralLedgerPendingEntryService generalLedgerPendingEntryService) { + this.generalLedgerPendingEntryService = generalLedgerPendingEntryService; + } + + public void setPurapAccountingService(final PurapAccountingService purapAccountingService) { + this.purapAccountingService = purapAccountingService; + } + + public void setUniversityDateService(final UniversityDateService universityDateService) { + this.universityDateService = universityDateService; + } + + public void setPurchaseOrderService(final PurchaseOrderService purchaseOrderService) { + this.purchaseOrderService = purchaseOrderService; + } + + public void setObjectCodeService(final ObjectCodeService objectCodeService) { + this.objectCodeService = objectCodeService; + } + + public void setPaymentRequestService(final PaymentRequestService paymentRequestService) { + this.paymentRequestService = paymentRequestService; + } + + public void setPurapAccountRevisionService(final PurapAccountRevisionService purapAccountRevisionService) { + this.purapAccountRevisionService = purapAccountRevisionService; + } +} diff --git a/src/main/resources/edu/cornell/kfs/module/cg/cu-ojb-cg.xml b/src/main/resources/edu/cornell/kfs/module/cg/cu-ojb-cg.xml index 2821b7a574..7e4001f00d 100644 --- a/src/main/resources/edu/cornell/kfs/module/cg/cu-ojb-cg.xml +++ b/src/main/resources/edu/cornell/kfs/module/cg/cu-ojb-cg.xml @@ -42,7 +42,7 @@ - + @@ -76,9 +76,12 @@ - - - + + + + From ddd33821d849290001063c5a90bdec08e48b5db5 Mon Sep 17 00:00:00 2001 From: Daniela Balmus Date: Thu, 12 Feb 2026 10:10:09 +0000 Subject: [PATCH 11/12] KFSPTS-36246 add more code changes --- .../sys/businessobject/datadictionary/CreateDoneBatchFile.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/resources/edu/cornell/kfs/sys/businessobject/datadictionary/CreateDoneBatchFile.xml b/src/main/resources/edu/cornell/kfs/sys/businessobject/datadictionary/CreateDoneBatchFile.xml index ab1418ef43..a710e0777c 100644 --- a/src/main/resources/edu/cornell/kfs/sys/businessobject/datadictionary/CreateDoneBatchFile.xml +++ b/src/main/resources/edu/cornell/kfs/sys/businessobject/datadictionary/CreateDoneBatchFile.xml @@ -46,9 +46,6 @@ - - From 00cff792a2d20136ced00e40d3afbaefa1cbbcf9 Mon Sep 17 00:00:00 2001 From: Daniela Balmus Date: Wed, 11 Mar 2026 11:02:32 +0000 Subject: [PATCH 12/12] KFSPTS-36246 unit test --- .../sys/businessobject/service/impl/BatchFileSearchService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/kuali/kfs/sys/businessobject/service/impl/BatchFileSearchService.java b/src/main/java/org/kuali/kfs/sys/businessobject/service/impl/BatchFileSearchService.java index 7fc2f1fdf4..a923c1db94 100644 --- a/src/main/java/org/kuali/kfs/sys/businessobject/service/impl/BatchFileSearchService.java +++ b/src/main/java/org/kuali/kfs/sys/businessobject/service/impl/BatchFileSearchService.java @@ -308,7 +308,7 @@ protected static List getDirectoriesToSearch(final List userSelected List directoriesToSearch = new ArrayList<>(); if (CollectionUtils.isNotEmpty(userSelectedPaths)) { for (final Path uniqueSelectedDirectory : uniqueSelectedDirectories) { - final Path directory = BatchFileUtils.resolvePathToAbsolutePath(uniqueSelectedDirectory); + final Path directory = BatchFileUtils.resolvePathToAbsolutePath(uniqueSelectedDirectory.getFileName()); if (Files.exists(directory)) { directoriesToSearch.add(directory); }