diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/watersupply/WaterContractDao.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/watersupply/WaterContractDao.java index 7fd17c287..e9524e6cb 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dao/watersupply/WaterContractDao.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/watersupply/WaterContractDao.java @@ -150,7 +150,7 @@ public void renameWaterContract(WaterUser waterUser, String oldContractName, String newContractName) { connection(dsl, c -> { setOffice(c, waterUser.getProjectId().getOfficeId()); - WATER_USER_OBJ_T waterUserT = WaterSupplyUtils.toWaterUser(waterUser); + WATER_USER_OBJ_T waterUserT = WaterSupplyUtils.toWaterUserObjT(waterUser); WATER_USER_CONTRACT_REF_T waterUserContract = new WATER_USER_CONTRACT_REF_T(waterUserT, oldContractName); CWMS_WATER_SUPPLY_PACKAGE.call_RENAME_CONTRACT(DSL.using(c).configuration(), waterUserContract, oldContractName, newContractName); @@ -169,7 +169,7 @@ public void deleteWaterUser(CwmsId location, String entityName, DeleteMethod del public void deleteWaterContract(WaterUserContract contract, DeleteMethod deleteAction) { connection(dsl, c -> { setOffice(c, contract.getOfficeId()); - WATER_USER_OBJ_T waterUserT = WaterSupplyUtils.toWaterUser(contract.getWaterUser()); + WATER_USER_OBJ_T waterUserT = WaterSupplyUtils.toWaterUserObjT(contract.getWaterUser()); String contractName = contract.getContractId().getName(); WATER_USER_CONTRACT_REF_T waterUserContract = new WATER_USER_CONTRACT_REF_T(waterUserT, contractName); CWMS_WATER_SUPPLY_PACKAGE.call_DELETE_CONTRACT(DSL.using(c).configuration(), waterUserContract, diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/watersupply/WaterSupplyAccountingDao.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/watersupply/WaterSupplyAccountingDao.java new file mode 100644 index 000000000..48b429d31 --- /dev/null +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/watersupply/WaterSupplyAccountingDao.java @@ -0,0 +1,99 @@ +/* + * + * MIT License + * + * Copyright (c) 2024 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dao.watersupply; + +import cwms.cda.data.dao.JooqDao; +import cwms.cda.data.dto.CwmsId; +import cwms.cda.data.dto.watersupply.WaterSupplyAccounting; +import cwms.cda.data.dto.watersupply.WaterUser; +import hec.lang.Const; +import java.math.BigInteger; +import java.sql.Timestamp; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import org.jooq.DSLContext; +import org.jooq.impl.DSL; +import usace.cwms.db.jooq.codegen.packages.CWMS_WATER_SUPPLY_PACKAGE; +import usace.cwms.db.jooq.codegen.udt.records.LOC_REF_TIME_WINDOW_TAB_T; +import usace.cwms.db.jooq.codegen.udt.records.WATER_USER_CONTRACT_REF_T; +import usace.cwms.db.jooq.codegen.udt.records.WAT_USR_CONTRACT_ACCT_TAB_T; + + +public class WaterSupplyAccountingDao extends JooqDao { + + public WaterSupplyAccountingDao(DSLContext dsl) { + super(dsl); + } + + public void storeAccounting(WaterSupplyAccounting accounting) { + String volumeUnitId = null; + String storeRule = Const.Delete_Insert; + boolean overrideProtection = false; + + connection(dsl, c -> { + setOffice(c, accounting.getWaterUser().getProjectId().getOfficeId()); + + WAT_USR_CONTRACT_ACCT_TAB_T accountingTab = WaterSupplyUtils.toWaterUserContractAcctTs(accounting); + WATER_USER_CONTRACT_REF_T contractRefT = WaterSupplyUtils + .toContractRef(accounting.getWaterUser(), accounting.getContractName()); + LOC_REF_TIME_WINDOW_TAB_T pumpTimeWindowTab = WaterSupplyUtils.toTimeWindowTabT(accounting); + String timeZoneId = "UTC"; + String overrideProt = formatBool(overrideProtection); + CWMS_WATER_SUPPLY_PACKAGE.call_STORE_ACCOUNTING_SET(DSL.using(c).configuration(), accountingTab, + contractRefT, pumpTimeWindowTab, timeZoneId, volumeUnitId, storeRule, overrideProt); + }); + } + + public List retrieveAccounting(String contractName, WaterUser waterUser, + CwmsId projectLocation, String units, Instant startTime, Instant endTime, + boolean startInclusive, boolean endInclusive, boolean ascendingFlag, int rowLimit) { + + String transferType = null; + WATER_USER_CONTRACT_REF_T contractRefT = WaterSupplyUtils.toContractRef(waterUser, contractName); + Timestamp startTimestamp = Timestamp.from(startTime); + Timestamp endTimestamp = Timestamp.from(endTime); + String timeZoneId = "UTC"; + String startInclusiveFlag = formatBool(startInclusive); + String endInclusiveFlag = formatBool(endInclusive); + String ascendingFlagStr = formatBool(ascendingFlag); + BigInteger rowLimitBigInt = BigInteger.valueOf(rowLimit); + + return connectionResult(dsl, c -> { + setOffice(c, projectLocation.getOfficeId()); + WAT_USR_CONTRACT_ACCT_TAB_T watUsrContractAcctObjTs + = CWMS_WATER_SUPPLY_PACKAGE.call_RETRIEVE_ACCOUNTING_SET(DSL.using(c).configuration(), + contractRefT, units, startTimestamp, endTimestamp, timeZoneId, startInclusiveFlag, + endInclusiveFlag, ascendingFlagStr, rowLimitBigInt, transferType); + if (!watUsrContractAcctObjTs.isEmpty()) { + return WaterSupplyUtils.toWaterSupplyAccountingList(c, watUsrContractAcctObjTs); + } else { + return new ArrayList<>(); + } + }); + } +} diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dao/watersupply/WaterSupplyUtils.java b/cwms-data-api/src/main/java/cwms/cda/data/dao/watersupply/WaterSupplyUtils.java index 7cffae116..43c95dd8c 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dao/watersupply/WaterSupplyUtils.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dao/watersupply/WaterSupplyUtils.java @@ -29,29 +29,48 @@ import cwms.cda.data.dao.JooqDao; import cwms.cda.data.dao.location.kind.LocationUtil; import cwms.cda.data.dto.CwmsId; +import cwms.cda.data.dto.Location; import cwms.cda.data.dto.LookupType; +import cwms.cda.data.dto.watersupply.PumpLocation; +import cwms.cda.data.dto.watersupply.PumpTransfer; import cwms.cda.data.dto.watersupply.PumpType; +import cwms.cda.data.dto.watersupply.WaterSupplyAccounting; import cwms.cda.data.dto.watersupply.WaterSupplyPump; import cwms.cda.data.dto.watersupply.WaterUser; import cwms.cda.data.dto.watersupply.WaterUserContract; +import java.sql.Connection; import java.sql.Timestamp; +import java.time.Instant; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.jetbrains.annotations.NotNull; +import org.jooq.impl.DSL; +import usace.cwms.db.jooq.codegen.udt.records.LOCATION_REF_T; +import usace.cwms.db.jooq.codegen.udt.records.LOC_REF_TIME_WINDOW_OBJ_T; +import usace.cwms.db.jooq.codegen.udt.records.LOC_REF_TIME_WINDOW_TAB_T; import usace.cwms.db.jooq.codegen.udt.records.LOOKUP_TYPE_OBJ_T; import usace.cwms.db.jooq.codegen.udt.records.LOOKUP_TYPE_TAB_T; import usace.cwms.db.jooq.codegen.udt.records.WATER_USER_CONTRACT_OBJ_T; import usace.cwms.db.jooq.codegen.udt.records.WATER_USER_CONTRACT_REF_T; import usace.cwms.db.jooq.codegen.udt.records.WATER_USER_CONTRACT_TAB_T; import usace.cwms.db.jooq.codegen.udt.records.WATER_USER_OBJ_T; +import usace.cwms.db.jooq.codegen.udt.records.WAT_USR_CONTRACT_ACCT_OBJ_T; +import usace.cwms.db.jooq.codegen.udt.records.WAT_USR_CONTRACT_ACCT_TAB_T; final class WaterSupplyUtils { + private static final Logger LOGGER = Logger.getLogger(WaterSupplyUtils.class.getName()); private WaterSupplyUtils() { throw new IllegalStateException("Utility class"); } - public static WaterUserContract toWaterContract(WATER_USER_CONTRACT_OBJ_T contract) { + static WaterUserContract toWaterContract(WATER_USER_CONTRACT_OBJ_T contract) { return new WaterUserContract.Builder().withContractedStorage(contract.getCONTRACTED_STORAGE()) .withTotalAllocPercentActivated(contract.getTOTAL_ALLOC_PERCENT_ACTIVATED()) .withContractType(LocationUtil.getLookupType(contract.getWATER_SUPPLY_CONTRACT_TYPE())) @@ -79,7 +98,7 @@ public static WaterUserContract toWaterContract(WATER_USER_CONTRACT_OBJ_T contra .build(); } - public static WaterUser toWaterUser(WATER_USER_OBJ_T waterUserTabT) { + static WaterUser toWaterUser(WATER_USER_OBJ_T waterUserTabT) { return new WaterUser.Builder().withEntityName(waterUserTabT.getENTITY_NAME()) .withProjectId(new CwmsId.Builder().withName(waterUserTabT.getPROJECT_LOCATION_REF() .call_GET_LOCATION_ID()).withOfficeId(waterUserTabT.getPROJECT_LOCATION_REF() @@ -87,7 +106,7 @@ public static WaterUser toWaterUser(WATER_USER_OBJ_T waterUserTabT) { .withWaterRight(waterUserTabT.getWATER_RIGHT()).build(); } - public static WATER_USER_OBJ_T toWaterUser(WaterUser waterUser) { + static WATER_USER_OBJ_T toWaterUserObjT(WaterUser waterUser) { WATER_USER_OBJ_T waterUserObjT = new WATER_USER_OBJ_T(); waterUserObjT.setENTITY_NAME(waterUser.getEntityName()); waterUserObjT.setPROJECT_LOCATION_REF(LocationUtil.getLocationRef(waterUser.getProjectId())); @@ -95,15 +114,7 @@ public static WATER_USER_OBJ_T toWaterUser(WaterUser waterUser) { return waterUserObjT; } - public static WATER_USER_OBJ_T toWaterUserObjT(WaterUser waterUser) { - WATER_USER_OBJ_T waterUserObjT = new WATER_USER_OBJ_T(); - waterUserObjT.setENTITY_NAME(waterUser.getEntityName()); - waterUserObjT.setPROJECT_LOCATION_REF(LocationUtil.getLocationRef(waterUser.getProjectId())); - waterUserObjT.setWATER_RIGHT(waterUser.getWaterRight()); - return waterUserObjT; - } - - public static LOOKUP_TYPE_OBJ_T toLookupTypeO(LookupType lookupType) { + static LOOKUP_TYPE_OBJ_T toLookupTypeO(LookupType lookupType) { LOOKUP_TYPE_OBJ_T lookupTypeObjT = new LOOKUP_TYPE_OBJ_T(); lookupTypeObjT.setOFFICE_ID(lookupType.getOfficeId()); lookupTypeObjT.setDISPLAY_VALUE(lookupType.getDisplayValue()); @@ -112,27 +123,27 @@ public static LOOKUP_TYPE_OBJ_T toLookupTypeO(LookupType lookupType) { return lookupTypeObjT; } - public static LOOKUP_TYPE_TAB_T toLookupTypeT(LookupType lookupType) { + static LOOKUP_TYPE_TAB_T toLookupTypeT(LookupType lookupType) { List lookupTypeList = new ArrayList<>(); lookupTypeList.add(toLookupTypeO(lookupType)); return new LOOKUP_TYPE_TAB_T(lookupTypeList); } - public static WATER_USER_CONTRACT_REF_T toContractRef(WaterUser waterUser, String contractName) { + static WATER_USER_CONTRACT_REF_T toContractRef(WaterUser waterUser, String contractName) { WATER_USER_CONTRACT_REF_T waterUserContractRefT = new WATER_USER_CONTRACT_REF_T(); - waterUserContractRefT.setWATER_USER(toWaterUser(waterUser)); + waterUserContractRefT.setWATER_USER(toWaterUserObjT(waterUser)); waterUserContractRefT.setCONTRACT_NAME(contractName); return waterUserContractRefT; } - public static WATER_USER_CONTRACT_REF_T toWaterUserContractRefTs(WaterUserContract waterUserContract) { + static WATER_USER_CONTRACT_REF_T toWaterUserContractRefTs(WaterUserContract waterUserContract) { WATER_USER_CONTRACT_REF_T waterUserContractRefT = new WATER_USER_CONTRACT_REF_T(); - waterUserContractRefT.setWATER_USER(toWaterUser(waterUserContract.getWaterUser())); + waterUserContractRefT.setWATER_USER(toWaterUserObjT(waterUserContract.getWaterUser())); waterUserContractRefT.setCONTRACT_NAME(waterUserContract.getContractId().getName()); return waterUserContractRefT; } - public static WATER_USER_CONTRACT_TAB_T toWaterUserContractTs(WaterUserContract waterUserContract) { + static WATER_USER_CONTRACT_TAB_T toWaterUserContractTs(WaterUserContract waterUserContract) { WATER_USER_CONTRACT_OBJ_T waterUserContractObjT = new WATER_USER_CONTRACT_OBJ_T(); waterUserContractObjT.setCONTRACTED_STORAGE(waterUserContract.getContractedStorage()); waterUserContractObjT.setTOTAL_ALLOC_PERCENT_ACTIVATED(waterUserContract.getTotalAllocPercentActivated()); @@ -158,4 +169,262 @@ public static WATER_USER_CONTRACT_TAB_T toWaterUserContractTs(WaterUserContract contractList.add(waterUserContractObjT); return new WATER_USER_CONTRACT_TAB_T(contractList); } + + static WAT_USR_CONTRACT_ACCT_TAB_T toWaterUserContractAcctTs(WaterSupplyAccounting accounting) { + List watUsrContractAcctObjTList = new ArrayList<>(); + LOCATION_REF_T pumpIn = LocationUtil.getLocationRef(accounting.getPumpLocations().getPumpIn()); + LOCATION_REF_T pumpOut = LocationUtil.getLocationRef(accounting.getPumpLocations().getPumpOut()); + LOCATION_REF_T pumpBelow = LocationUtil.getLocationRef(accounting.getPumpLocations().getPumpBelow()); + + for (Map.Entry> entry : accounting.getPumpAccounting().entrySet()) { + for (PumpTransfer transfer : entry.getValue()) { + WAT_USR_CONTRACT_ACCT_OBJ_T watUsrContractAcctObjT = new WAT_USR_CONTRACT_ACCT_OBJ_T(); + WATER_USER_CONTRACT_REF_T contractRef = toContractRef(accounting.getWaterUser(), + accounting.getContractName()); + watUsrContractAcctObjT.setWATER_USER_CONTRACT_REF(contractRef); + watUsrContractAcctObjT.setACCOUNTING_REMARKS(transfer.getComment()); + watUsrContractAcctObjT.setPUMP_FLOW(transfer.getFlow()); + LOOKUP_TYPE_OBJ_T transferType = toLookupTypeO(new LookupType.Builder() + .withDisplayValue(transfer.getTransferTypeDisplay()) + .withActive(true) + .withOfficeId(accounting.getWaterUser().getProjectId().getOfficeId()) + .build()); + watUsrContractAcctObjT.setPHYSICAL_TRANSFER_TYPE(transferType); + switch (transfer.getPumpType()) { + case IN: + watUsrContractAcctObjT.setPUMP_LOCATION_REF(pumpIn); + break; + case OUT: + watUsrContractAcctObjT.setPUMP_LOCATION_REF(pumpOut); + break; + case BELOW: + watUsrContractAcctObjT.setPUMP_LOCATION_REF(pumpBelow); + break; + default: + LOGGER.log(Level.WARNING, "Invalid pump type"); + throw new IllegalArgumentException( + String.format("Invalid pump type for mapping to DB object: %s", transfer.getPumpType())); + } + watUsrContractAcctObjT.setTRANSFER_START_DATETIME(Timestamp.from(entry.getKey())); + watUsrContractAcctObjTList.add(watUsrContractAcctObjT); + } + } + return new WAT_USR_CONTRACT_ACCT_TAB_T(watUsrContractAcctObjTList); + } + + static LOC_REF_TIME_WINDOW_TAB_T toTimeWindowTabT(WaterSupplyAccounting accounting) { + List timeWindowList = new ArrayList<>(); + LOCATION_REF_T pumpIn = LocationUtil.getLocationRef(accounting.getPumpLocations().getPumpIn()); + LOCATION_REF_T pumpOut = LocationUtil.getLocationRef(accounting.getPumpLocations().getPumpOut()); + LOCATION_REF_T pumpBelow = LocationUtil.getLocationRef(accounting.getPumpLocations().getPumpBelow()); + + for (Map.Entry> entry : accounting.getPumpAccounting().entrySet()) { + for (PumpTransfer transfer : entry.getValue()) { + LOC_REF_TIME_WINDOW_OBJ_T timeWindow = new LOC_REF_TIME_WINDOW_OBJ_T(); + switch (transfer.getPumpType()) { + case IN: + timeWindow.setLOCATION_REF(pumpIn); + break; + case OUT: + timeWindow.setLOCATION_REF(pumpOut); + break; + case BELOW: + timeWindow.setLOCATION_REF(pumpBelow); + break; + default: + LOGGER.log(Level.WARNING, "Invalid pump type"); + break; + } + timeWindow.setSTART_DATE(Timestamp.from(entry.getKey())); + timeWindow.setEND_DATE(Timestamp.from(entry.getKey())); + timeWindowList.add(timeWindow); + } + } + return new LOC_REF_TIME_WINDOW_TAB_T(timeWindowList); + } + + static List toWaterSupplyAccountingList(Connection c, WAT_USR_CONTRACT_ACCT_TAB_T + watUsrContractAcctTabT) { + + List waterSupplyAccounting = new ArrayList<>(); + Map cacheMap = new TreeMap<>(); + + for (WAT_USR_CONTRACT_ACCT_OBJ_T watUsrContractAcctObjT : watUsrContractAcctTabT) { + WATER_USER_CONTRACT_REF_T watUsrContractRef = watUsrContractAcctObjT.getWATER_USER_CONTRACT_REF(); + AccountingKey key = new AccountingKey.Builder() + .withContractName(watUsrContractRef.getCONTRACT_NAME()) + .withWaterUser(new WaterUser.Builder() + .withWaterRight(watUsrContractRef.getWATER_USER().getWATER_RIGHT()) + .withEntityName(watUsrContractRef.getWATER_USER().getENTITY_NAME()) + .withProjectId(CwmsId.buildCwmsId(watUsrContractRef.getWATER_USER().getPROJECT_LOCATION_REF().getOFFICE_ID(), + watUsrContractRef.getWATER_USER().getPROJECT_LOCATION_REF().call_GET_LOCATION_ID())) + .build()) + .build(); + if (cacheMap.containsKey(key)) { + WaterSupplyAccounting accounting = cacheMap.get(key); + addTransfer(watUsrContractAcctObjT, accounting); + } else { + cacheMap.put(key, createAccounting(c, watUsrContractAcctObjT)); + } + } + for (Map.Entry entry : cacheMap.entrySet()) { + waterSupplyAccounting.add(entry.getValue()); + } + return waterSupplyAccounting; + } + + private static WaterSupplyAccounting createAccounting(Connection c, WAT_USR_CONTRACT_ACCT_OBJ_T acctObjT) { + WaterContractDao waterContractDao = new WaterContractDao(DSL.using(c)); + WATER_USER_OBJ_T waterUserObjT = acctObjT.getWATER_USER_CONTRACT_REF().getWATER_USER(); + WaterUserContract waterUserContract = waterContractDao.getWaterContract( + acctObjT.getWATER_USER_CONTRACT_REF().getCONTRACT_NAME(), + new CwmsId.Builder() + .withOfficeId(waterUserObjT.getPROJECT_LOCATION_REF().getOFFICE_ID()) + .withName(waterUserObjT.getPROJECT_LOCATION_REF().call_GET_LOCATION_ID()) + .build(), + waterUserObjT.getENTITY_NAME()); + Map> pumpAccounting = new TreeMap<>(); + String pumpLocation = acctObjT.getPUMP_LOCATION_REF().call_GET_LOCATION_ID(); + String pumpOffice = acctObjT.getPUMP_LOCATION_REF().getOFFICE_ID(); + String transferDisplay = acctObjT.getPHYSICAL_TRANSFER_TYPE().getDISPLAY_VALUE(); + Location pumpIn = waterUserContract.getPumpInLocation() != null + ? waterUserContract.getPumpInLocation().getPumpLocation() : null; + Location pumpOut = waterUserContract.getPumpOutLocation() != null + ? waterUserContract.getPumpOutLocation().getPumpLocation() : null; + Location pumpBelow = waterUserContract.getPumpOutBelowLocation() != null + ? waterUserContract.getPumpOutBelowLocation().getPumpLocation() : null; + Instant transferStart = acctObjT.getTRANSFER_START_DATETIME().toInstant(); + String remarks = acctObjT.getACCOUNTING_REMARKS(); + double flow = acctObjT.getPUMP_FLOW(); + + PumpTransfer transfer = null; + if (pumpIn != null && pumpIn.getName().equalsIgnoreCase(pumpLocation) + && pumpIn.getOfficeId().equalsIgnoreCase(pumpOffice)) { + transfer = new PumpTransfer(PumpType.IN, transferDisplay, flow, remarks); + } else if (pumpOut != null && pumpOut.getName().equalsIgnoreCase(pumpLocation) + && pumpOut.getOfficeId().equalsIgnoreCase(pumpOffice)) { + transfer = new PumpTransfer(PumpType.OUT, transferDisplay, flow, remarks); + } else if (pumpBelow != null && pumpBelow.getName().equalsIgnoreCase(pumpLocation) + && pumpBelow.getOfficeId().equalsIgnoreCase(pumpOffice)) { + transfer = new PumpTransfer(PumpType.BELOW, transferDisplay, flow, remarks); + } + if (transfer != null) { + pumpAccounting.put(transferStart, Collections.singletonList(transfer)); + } + return new WaterSupplyAccounting.Builder() + .withContractName(acctObjT.getWATER_USER_CONTRACT_REF().getCONTRACT_NAME()) + .withWaterUser(toWaterUser(waterUserObjT)) + .withPumpLocations(new PumpLocation.Builder() + .withPumpIn(pumpIn != null ? CwmsId.buildCwmsId(pumpIn.getOfficeId(), pumpIn.getName()) : null) + .withPumpOut(pumpOut != null ? CwmsId.buildCwmsId(pumpOut.getOfficeId(), pumpOut.getName()) : null) + .withPumpBelow(pumpBelow != null ? CwmsId.buildCwmsId(pumpBelow.getOfficeId(), pumpBelow.getName()) : null) + .build()) + .withPumpAccounting(pumpAccounting) + .build(); + } + + private static void addTransfer(WAT_USR_CONTRACT_ACCT_OBJ_T acctObjTs, WaterSupplyAccounting accounting) { + PumpTransfer transfer = null; + String transferDisplay = acctObjTs.getPHYSICAL_TRANSFER_TYPE().getDISPLAY_VALUE(); + String accountingRemarks = acctObjTs.getACCOUNTING_REMARKS(); + Instant transferStart = acctObjTs.getTRANSFER_START_DATETIME().toInstant(); + String officeId = acctObjTs.getPUMP_LOCATION_REF().getOFFICE_ID(); + String locationId = acctObjTs.getPUMP_LOCATION_REF().call_GET_LOCATION_ID(); + CwmsId pumpIn = accounting.getPumpLocations().getPumpIn(); + CwmsId pumpOut = accounting.getPumpLocations().getPumpOut(); + CwmsId pumpBelow = accounting.getPumpLocations().getPumpBelow(); + + if (pumpIn != null && pumpIn.getName().equalsIgnoreCase(locationId) + && pumpIn.getOfficeId().equalsIgnoreCase(officeId)) { + transfer = new PumpTransfer(PumpType.IN, transferDisplay, acctObjTs.getPUMP_FLOW(), accountingRemarks); + } else if (pumpOut != null && pumpOut.getName().equalsIgnoreCase(locationId) + && pumpOut.getOfficeId().equalsIgnoreCase(officeId)) { + transfer = new PumpTransfer(PumpType.OUT, transferDisplay, acctObjTs.getPUMP_FLOW(), accountingRemarks); + } else if (pumpBelow != null && pumpBelow.getName().equalsIgnoreCase(locationId) + && pumpBelow.getOfficeId().equalsIgnoreCase(officeId)) { + transfer = new PumpTransfer(PumpType.BELOW, transferDisplay, + acctObjTs.getPUMP_FLOW(), accountingRemarks); + } + if (accounting.getPumpAccounting().get(transferStart) != null) { + List transfers = new ArrayList<>(accounting.getPumpAccounting().get(transferStart)); + transfers.add(transfer); + accounting.getPumpAccounting().put(transferStart, transfers); + return; + } + accounting.getPumpAccounting().put(transferStart, + Collections.singletonList(transfer)); + } + + static class AccountingKey implements Comparable { + private final WaterUser waterUser; + private final String contractName; + + private AccountingKey(Builder builder) { + this.waterUser = builder.waterUser; + this.contractName = builder.contractName; + } + + WaterUser getWaterUser() { + return waterUser; + } + + String getContractName() { + return contractName; + } + + @Override + public int hashCode() { + return waterUser.hashCode() + contractName.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof AccountingKey)) { + return false; + } + AccountingKey key = (AccountingKey) obj; + return compareWaterUser(waterUser, key.getWaterUser()) && key.getContractName().equals(contractName); + } + + @Override + public int compareTo(@NotNull AccountingKey key) { + if (key == this) { + return 0; + } + int i = contractName.compareTo(key.getContractName()); + if (i == 0) { + i = compareWaterUser(waterUser, key.getWaterUser()) ? 0 : 1; + } + return i; + } + + private boolean compareWaterUser(WaterUser waterUser1, WaterUser waterUser2) { + return waterUser1.getEntityName().equals(waterUser2.getEntityName()) + && waterUser1.getProjectId().getName().equals(waterUser2.getProjectId().getName()) + && waterUser1.getProjectId().getOfficeId().equals(waterUser2.getProjectId().getOfficeId()) + && waterUser1.getWaterRight().equals(waterUser2.getWaterRight()); + } + + private static final class Builder { + private WaterUser waterUser; + private String contractName; + + Builder withWaterUser(WaterUser waterUser) { + this.waterUser = waterUser; + return this; + } + + Builder withContractName(String contractName) { + this.contractName = contractName; + return this; + } + + AccountingKey build() { + return new AccountingKey(this); + } + } + } } diff --git a/cwms-data-api/src/main/java/cwms/cda/data/dto/watersupply/WaterSupplyAccounting.java b/cwms-data-api/src/main/java/cwms/cda/data/dto/watersupply/WaterSupplyAccounting.java index f57382b85..0e5aa28ad 100644 --- a/cwms-data-api/src/main/java/cwms/cda/data/dto/watersupply/WaterSupplyAccounting.java +++ b/cwms-data-api/src/main/java/cwms/cda/data/dto/watersupply/WaterSupplyAccounting.java @@ -43,7 +43,6 @@ @FormattableWith(contentType = Formats.JSONV1, formatter = JsonV1.class, aliases = {Formats.DEFAULT, Formats.JSON}) @JsonDeserialize(builder = WaterSupplyAccounting.Builder.class) -@FormattableWith(contentType = Formats.JSONV1, formatter = JsonV1.class) @JsonNaming(PropertyNamingStrategies.KebabCaseStrategy.class) public final class WaterSupplyAccounting extends CwmsDTOBase { @JsonProperty(required = true) diff --git a/cwms-data-api/src/test/java/cwms/cda/data/dao/watersupply/WaterSupplyAccountingDaoIT.java b/cwms-data-api/src/test/java/cwms/cda/data/dao/watersupply/WaterSupplyAccountingDaoIT.java new file mode 100644 index 000000000..eb5263b1d --- /dev/null +++ b/cwms-data-api/src/test/java/cwms/cda/data/dao/watersupply/WaterSupplyAccountingDaoIT.java @@ -0,0 +1,694 @@ +/* + * + * MIT License + * + * Copyright (c) 2024 Hydrologic Engineering Center + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE + * SOFTWARE. + */ + +package cwms.cda.data.dao.watersupply; + +import cwms.cda.api.DataApiTestIT; +import cwms.cda.api.enums.Nation; +import cwms.cda.data.dao.DeleteRule; +import cwms.cda.data.dao.JooqDao; +import cwms.cda.data.dao.LocationsDaoImpl; +import cwms.cda.data.dao.LookupTypeDao; +import cwms.cda.data.dao.project.ProjectDao; +import cwms.cda.data.dto.CwmsId; +import cwms.cda.data.dto.Location; +import cwms.cda.data.dto.LookupType; +import cwms.cda.data.dto.project.Project; +import cwms.cda.data.dto.watersupply.PumpLocation; +import cwms.cda.data.dto.watersupply.PumpTransfer; +import cwms.cda.data.dto.watersupply.PumpType; +import cwms.cda.data.dto.watersupply.WaterSupplyAccounting; +import cwms.cda.data.dto.watersupply.WaterSupplyPump; +import cwms.cda.data.dto.watersupply.WaterUser; +import cwms.cda.data.dto.watersupply.WaterUserContract; +import cwms.cda.helpers.DTOMatch; +import fixtures.CwmsDataApiSetupCallback; +import mil.army.usace.hec.test.database.CwmsDatabaseContainer; +import org.jooq.DSLContext; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EnumSource; +import java.io.IOException; +import java.math.BigDecimal; +import java.time.Instant; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import static cwms.cda.data.dao.JooqDao.getDslContext; +import static org.junit.jupiter.api.Assertions.*; + +@Tag("integration") +class WaterSupplyAccountingDaoIT extends DataApiTestIT { + private static final String OFFICE_ID = "SPK"; + private static final JooqDao.DeleteMethod TEST_DELETE_ACTION = JooqDao.DeleteMethod.DELETE_ALL; + private static final String PROJECT_NAME = "Test Project Name"; + private static final String PROJECT_NAME_2 = "Test Project Name 2"; + private static final String WATER_USER_ENTITY_NAME = "Test entity"; + private static final Location testLocation = buildTestLocation(PROJECT_NAME, "Test Location"); + private static final Location testLocation2 = buildTestLocation(PROJECT_NAME_2, "Test Location 2"); + private static final Project testProject = buildTestProject(PROJECT_NAME); + private static final Project testProject2 = buildTestProject(PROJECT_NAME_2); + private static final WaterUser testUser = buildTestWaterUser(WATER_USER_ENTITY_NAME, PROJECT_NAME); + private static final WaterUser testUser2 = buildTestWaterUser(WATER_USER_ENTITY_NAME, PROJECT_NAME_2); + private static final WaterUserContract contract = buildTestWaterContract("Contract Name", testUser, true); + private static final WaterUserContract contract2 = buildTestWaterContract("Contract Name 2", testUser2, false); + private static final LookupType testTransferType = new LookupType.Builder() + .withDisplayValue("Pipeline") + .withTooltip("Test Location Tooltip") + .withActive(true) + .withOfficeId(OFFICE_ID) + .build(); + + @BeforeAll + static void setup() throws Exception { + CwmsDatabaseContainer db = CwmsDataApiSetupCallback.getDatabaseLink(); + db.connection(c -> { + DSLContext ctx = getDslContext(c, OFFICE_ID); + LocationsDaoImpl dao = new LocationsDaoImpl(ctx); + ProjectDao projectDao = new ProjectDao(ctx); + WaterContractDao contractDao = new WaterContractDao(ctx); + LookupTypeDao lookupTypeDao = new LookupTypeDao(ctx); + try { + dao.storeLocation(testLocation); + dao.storeLocation(testLocation2); + lookupTypeDao.storeLookupType("AT_PHYSICAL_TRANSFER_TYPE", "PHYS_TRANS_TYPE", testTransferType); + projectDao.store(testProject, true); + projectDao.store(testProject2, true); + contractDao.storeWaterUser(testUser, true); + contractDao.storeWaterUser(testUser2, true); + contractDao.storeWaterContract(contract, true, false); + contractDao.storeWaterContract(contract2, true, false); + } catch (IOException e) { + throw new RuntimeException("Failed to store location or project", e); + } + }); + } + + @AfterAll + static void tearDown() throws Exception { + CwmsDatabaseContainer db = CwmsDataApiSetupCallback.getDatabaseLink(); + db.connection(c -> { + DSLContext ctx = getDslContext(c, OFFICE_ID); + LocationsDaoImpl dao = new LocationsDaoImpl(ctx); + ProjectDao projectDao = new ProjectDao(ctx); + LookupTypeDao lookupTypeDao = new LookupTypeDao(ctx); + WaterContractDao contractDao = new WaterContractDao(ctx); + contractDao.deleteWaterContract(contract, TEST_DELETE_ACTION); + contractDao.deleteWaterContract(contract2, TEST_DELETE_ACTION); + contractDao.deleteWaterUser(testUser.getProjectId(), testUser.getEntityName(), TEST_DELETE_ACTION); + contractDao.deleteWaterUser(testUser2.getProjectId(), testUser2.getEntityName(), TEST_DELETE_ACTION); + lookupTypeDao.deleteLookupType("AT_PHYSICAL_TRANSFER_TYPE", "PHYS_TRANS_TYPE", + OFFICE_ID, testTransferType.getDisplayValue()); + projectDao.delete(testProject.getLocation().getOfficeId(), testProject.getLocation().getName(), + DeleteRule.DELETE_ALL); + projectDao.delete(testProject2.getLocation().getOfficeId(), testProject2.getLocation().getName(), + DeleteRule.DELETE_ALL); + dao.deleteLocation(testLocation.getName(), OFFICE_ID); + dao.deleteLocation(testLocation2.getName(), OFFICE_ID); + }); + } + + @ParameterizedTest + @EnumSource(TestDates.class) + void testStoreAndRetrieveWaterSupplyPumpAccounting(TestDates testDates) throws Exception { + + // Test Structure + // 1) Create and store a Water Supply Contract + // 2) Create and store Water Supply Pump Accounting + // 3) Retrieve Water Supply Pump Accounting and assert it is the same (or not in DB) + + WaterSupplyAccounting accounting = buildTestAccounting(); + Instant startTime = testDates.startTime(); + Instant endTime = testDates.endTime(); + boolean startInclusive = testDates.startInclusive(); + boolean endInclusive = testDates.endInclusive(); + boolean inDB = testDates.isInDb(); + CwmsDatabaseContainer db = CwmsDataApiSetupCallback.getDatabaseLink(); + db.connection(c -> { + DSLContext ctx = getDslContext(c, OFFICE_ID); + WaterSupplyAccountingDao accountingDao = new WaterSupplyAccountingDao(ctx); + accountingDao.storeAccounting(accounting); + }, CwmsDataApiSetupCallback.getWebUser()); + + int rowLimit = 20; + boolean headFlag = false; + + if (inDB) { + // retrieve and assert in db + assertPumpAccountingInDB(accounting, contract, startTime, endTime, startInclusive, + endInclusive, headFlag, rowLimit); + } else { + // retrieve and assert not in db + assertPumpAccountingInDBEmpty(contract, startTime, endTime, startInclusive, + endInclusive, headFlag, rowLimit); + } + } + + @Test + void testStoreAndRetrieveWithFewerPumps() throws Exception { + + // Test Structure + // 1) Create and store a Water Supply Contract + // 2) Create and store Water Supply Pump Accounting + // 3) Retrieve Water Supply Pump Accounting and assert it is the same (or not in DB) + + WaterSupplyAccounting accounting = buildTestAccountingWithFewerPumps(); + Instant startTime = Instant.parse("2015-10-01T00:00:00Z"); + Instant endTime = Instant.parse("2035-10-03T00:00:00Z"); + boolean startInclusive = true; + boolean endInclusive = true; + CwmsDatabaseContainer db = CwmsDataApiSetupCallback.getDatabaseLink(); + db.connection(c -> { + DSLContext ctx = getDslContext(c, OFFICE_ID); + WaterSupplyAccountingDao accountingDao = new WaterSupplyAccountingDao(ctx); + accountingDao.storeAccounting(accounting); + }, CwmsDataApiSetupCallback.getWebUser()); + + int rowLimit = 20; + boolean headFlag = false; + + // retrieve and assert in db + assertPumpAccountingInDB(accounting, contract2, startTime, endTime, startInclusive, + endInclusive, headFlag, rowLimit); + } + + @Test + void testStoreRetrievePaginated() throws Exception { + // Test Structure + // 1) Create and store a Water Supply Contract + // 2) Create and store Water Supply Pump Accounting + // 3) Retrieve Water Supply Pump Accounting and assert it is the same (or not in DB) + WaterSupplyAccounting accounting = buildTestAccounting(); + + Instant endTime = Instant.parse("2025-10-01T12:00:00Z"); + boolean startInclusive = true; + boolean endInclusive = true; + Instant startTime = Instant.parse("2025-10-01T00:00:00Z"); + + CwmsDatabaseContainer db = CwmsDataApiSetupCallback.getDatabaseLink(); + db.connection(c -> { + DSLContext ctx = getDslContext(c, OFFICE_ID); + WaterSupplyAccountingDao accountingDao = new WaterSupplyAccountingDao(ctx); + accountingDao.storeAccounting(accounting); + }, CwmsDataApiSetupCallback.getWebUser()); + + int rowLimit = 2; + boolean headFlag = false; + + Map> firstPageMap = new TreeMap<>(); + List transfers = accounting.getPumpAccounting().get(Instant.parse("2025-10-01T00:00:00Z")); + firstPageMap.put(Instant.parse("2025-10-01T00:00:00Z"), transfers); + + WaterSupplyAccounting accounting2 = new WaterSupplyAccounting.Builder() + .withWaterUser(accounting.getWaterUser()) + .withContractName(accounting.getContractName()) + .withWaterUser(accounting.getWaterUser()) + .withPumpLocations(accounting.getPumpLocations()) + .withPumpAccounting(firstPageMap) + .build(); + + // retrieve and assert in db + // page 1 + final Instant finalStartTime = startTime; + final Instant finalEndTime = endTime; + final boolean finalStartInclusive = startInclusive; + db.connection(c -> { + DSLContext ctx = getDslContext(c, OFFICE_ID); + WaterSupplyAccountingDao accountingDao = new WaterSupplyAccountingDao(ctx); + List pumpAccounting = accountingDao.retrieveAccounting(contract.getContractId() + .getName(), contract.getWaterUser(), contract.getWaterUser().getProjectId(), + null, finalStartTime, finalEndTime, finalStartInclusive, endInclusive, headFlag, rowLimit); + assertFalse(pumpAccounting.isEmpty()); + for (WaterSupplyAccounting returnedAccounting : pumpAccounting) { + assertNotNull(returnedAccounting.getPumpAccounting()); + DTOMatch.assertMatch(accounting2, returnedAccounting); + } + }, CwmsDataApiSetupCallback.getWebUser()); + + Map> secondPageMap = new TreeMap<>(); + List transfers2 = accounting.getPumpAccounting().get(Instant.parse("2025-10-02T00:00:00Z")); + secondPageMap.put(Instant.parse("2025-10-02T00:00:00Z"), transfers2); + + WaterSupplyAccounting accounting3 = new WaterSupplyAccounting.Builder() + .withWaterUser(accounting.getWaterUser()) + .withContractName(accounting.getContractName()) + .withWaterUser(accounting.getWaterUser()) + .withPumpLocations(accounting.getPumpLocations()) + .withPumpAccounting(secondPageMap) + .build(); + + startTime = Instant.parse("2025-10-01T12:00:00Z"); + endTime = Instant.parse("2025-10-03T00:00:00Z"); + startInclusive = false; + final Instant finalStartTime1 = startTime; + final Instant finalEndTime1 = endTime; + final boolean finalStartInclusive1 = startInclusive; + db.connection(c -> { + DSLContext ctx = getDslContext(c, OFFICE_ID); + WaterSupplyAccountingDao accountingDao = new WaterSupplyAccountingDao(ctx); + List pumpAccounting = accountingDao.retrieveAccounting(contract.getContractId() + .getName(), contract.getWaterUser(), contract.getWaterUser().getProjectId(), + null, finalStartTime1, finalEndTime1, finalStartInclusive1, endInclusive, headFlag, rowLimit); + assertFalse(pumpAccounting.isEmpty()); + for (WaterSupplyAccounting returnedAccounting : pumpAccounting) { + assertNotNull(returnedAccounting.getPumpAccounting()); + DTOMatch.assertMatch(accounting3, returnedAccounting); + } + }, CwmsDataApiSetupCallback.getWebUser()); + } + + @Test + void testStoreRetrieve() throws Exception { + WaterSupplyAccounting accounting = buildTestAccounting(); + final Instant startTime = Instant.parse("2015-10-01T00:00:00Z"); + final Instant endTime = Instant.parse("2035-10-03T00:00:00Z"); + final boolean startInclusive = false; + final boolean endInclusive = false; + final boolean headFlag = false; + final int rowLimit = 20; + CwmsDatabaseContainer db = CwmsDataApiSetupCallback.getDatabaseLink(); + db.connection(c -> { + DSLContext ctx = getDslContext(c, OFFICE_ID); + WaterSupplyAccountingDao accountingDao = new WaterSupplyAccountingDao(ctx); + accountingDao.storeAccounting(accounting); + List pumpAccounting = accountingDao.retrieveAccounting(contract.getContractId() + .getName(), contract.getWaterUser(), contract.getWaterUser().getProjectId(), + null, startTime, endTime, startInclusive, endInclusive, headFlag, rowLimit); + assertFalse(pumpAccounting.isEmpty()); + for (WaterSupplyAccounting returnedAccounting : pumpAccounting) { + assertNotNull(returnedAccounting.getPumpAccounting()); + DTOMatch.assertMatch(accounting, returnedAccounting); + } + }, CwmsDataApiSetupCallback.getWebUser()); + } + + private void assertPumpAccountingInDB(WaterSupplyAccounting expected, WaterUserContract waterContract, + Instant startTime, Instant endTime, boolean startInclusive, boolean endInclusive, boolean headFlag, + int rowLimit) throws Exception { + + CwmsDatabaseContainer db = CwmsDataApiSetupCallback.getDatabaseLink(); + db.connection(c -> { + DSLContext ctx = getDslContext(c, OFFICE_ID); + WaterSupplyAccountingDao accountingDao = new WaterSupplyAccountingDao(ctx); + List pumpAccounting = accountingDao.retrieveAccounting(waterContract.getContractId() + .getName(), waterContract.getWaterUser(), waterContract.getWaterUser().getProjectId(), + null, startTime, endTime, startInclusive, endInclusive, headFlag, rowLimit); + assertFalse(pumpAccounting.isEmpty()); + for (WaterSupplyAccounting returnedAccounting : pumpAccounting) { + assertNotNull(returnedAccounting.getPumpAccounting()); + DTOMatch.assertMatch(expected, returnedAccounting); + } + }, CwmsDataApiSetupCallback.getWebUser()); + } + + private void assertPumpAccountingInDBEmpty(WaterUserContract contract, + Instant startTime, Instant endTime, boolean startInclusive, boolean endInclusive, + boolean headFlag, int rowLimit) throws Exception { + CwmsDatabaseContainer db = CwmsDataApiSetupCallback.getDatabaseLink(); + db.connection(c -> { + DSLContext ctx = getDslContext(c, OFFICE_ID); + WaterSupplyAccountingDao accountingDao = new WaterSupplyAccountingDao(ctx); + List pumpAccounting = accountingDao.retrieveAccounting(contract.getContractId() + .getName(), contract.getWaterUser(), new CwmsId.Builder().withName(contract.getWaterUser() + .getProjectId().getName()).withOfficeId(OFFICE_ID).build(), + null, startTime, endTime, startInclusive, endInclusive, headFlag, rowLimit); + for (WaterSupplyAccounting returnedAccounting : pumpAccounting) { + assertTrue(returnedAccounting.getPumpAccounting().isEmpty()); + } + }, CwmsDataApiSetupCallback.getWebUser()); + } + + protected static WaterUser buildTestWaterUser(String entityName, String projectName) { + return new WaterUser.Builder() + .withEntityName(entityName) + .withProjectId(new CwmsId.Builder() + .withName(projectName) + .withOfficeId(OFFICE_ID) + .build()) + .withWaterRight("Test Water Right").build(); + } + + protected static WaterUserContract buildTestWaterContract(String contractName, WaterUser user, boolean allPumps) { + return new WaterUserContract.Builder() + .withContractType(new LookupType.Builder() + .withTooltip("Storage contract") + .withActive(true) + .withDisplayValue("Storage") + .withOfficeId(OFFICE_ID).build()) + .withStorageUnitsId("m3") + .withOfficeId(OFFICE_ID) + .withFutureUseAllocation(158900.6) + .withContractedStorage(1589005.6) + .withInitialUseAllocation(1500.2) + .withContractExpirationDate(Instant.ofEpochMilli(1979252516000L)) + .withContractEffectiveDate(Instant.ofEpochMilli(1766652851000L)) + .withTotalAllocPercentActivated(55.1) + .withContractId(new CwmsId.Builder() + .withName(contractName) + .withOfficeId(OFFICE_ID) + .build()) + .withFutureUsePercentActivated(35.7) + .withWaterUser(user) + .withPumpInLocation(new WaterSupplyPump.Builder() + .withPumpLocation(buildTestLocation(contractName + "-Pump 1", + "PUMP")).withPumpType(PumpType.IN).build()) + .withPumpOutLocation(allPumps ? new WaterSupplyPump.Builder() + .withPumpLocation(buildTestLocation(contractName + "-Pump 2", + "PUMP")).withPumpType(PumpType.OUT).build() : null) + .withPumpOutBelowLocation(allPumps ? new WaterSupplyPump.Builder() + .withPumpLocation(buildTestLocation(contractName + "-Pump 3", + "PUMP")).withPumpType(PumpType.BELOW).build() : null) + .build(); + + } + + protected static Location buildTestLocation(String locationName, String locationType) { + return new Location.Builder(OFFICE_ID, locationName) + .withBoundingOfficeId(OFFICE_ID) + .withMapLabel("Test Map Label") + .withElevation(150.6) + .withNation(Nation.US) + .withStateInitial("CA") + .withCountyName("Sacramento") + .withTimeZoneName(ZoneId.of("UTC")) + .withElevationUnits("m") + .withVerticalDatum("NGVD29") + .withHorizontalDatum("WGS84") + .withPublicName("Test Public Name") + .withLongName("Test Long Name") + .withDescription("Test Description") + .withNearestCity("Davis") + .withLatitude(35.6) + .withLongitude(-120.6) + .withPublishedLatitude(35.6) + .withPublishedLongitude(-120.6) + .withActive(true) + .withLocationType(locationType) + .withLocationKind(locationType) + .build(); + } + + protected static Project buildTestProject(String projectName) { + return new Project.Builder().withLocation(buildTestLocation(projectName, + "PROJECT")) + .withFederalCost(new BigDecimal("15980654.55")) + .build(); + } + + private WaterSupplyAccounting buildTestAccounting() { + return new WaterSupplyAccounting.Builder().withWaterUser(testUser) + .withContractName(contract.getContractId().getName()) + .withPumpAccounting(buildTestPumpAccountingList()) + .withPumpLocations(new PumpLocation.Builder() + .withPumpIn(CwmsId.buildCwmsId(OFFICE_ID, contract.getPumpInLocation().getPumpLocation().getName())) + .withPumpOut(CwmsId.buildCwmsId(OFFICE_ID, contract.getPumpOutLocation().getPumpLocation().getName())) + .withPumpBelow(CwmsId.buildCwmsId(OFFICE_ID, contract.getPumpOutBelowLocation().getPumpLocation().getName())) + .build()) + .build(); + } + + private WaterSupplyAccounting buildTestAccountingWithFewerPumps() { + return new WaterSupplyAccounting.Builder().withWaterUser(testUser2) + .withContractName(contract2.getContractId().getName()) + .withPumpAccounting(buildTestPumpAccountingListWithFewerPumps()) + .withPumpLocations(new PumpLocation.Builder() + .withPumpIn(CwmsId.buildCwmsId(OFFICE_ID, contract2.getPumpInLocation().getPumpLocation().getName())) + .build()) + .build(); + } + + private Map> buildTestPumpAccountingList() { + Map> retList = new TreeMap<>(); + List transfers = new ArrayList<>(); + transfers.add(new PumpTransfer(PumpType.IN, "Conduit", 100.0, "Test Transfer")); + transfers.add(new PumpTransfer(PumpType.OUT, "Pipeline", 200.0, "Emergency Transfer")); + retList.put(Instant.parse("2025-10-01T00:00:00Z"), transfers); + transfers.clear(); + transfers.add(new PumpTransfer(PumpType.OUT, "Canal", 300.0, "Test Transfer")); + transfers.add(new PumpTransfer(PumpType.BELOW, "Stream", 400.0, "Emergency Transfer")); + retList.put(Instant.parse("2025-10-02T00:00:00Z"), transfers); + return retList; + } + + private Map> buildTestPumpAccountingListWithFewerPumps() { + Map> retList = new TreeMap<>(); + retList.put(Instant.parse("2025-10-01T00:00:00Z"), + Collections.singletonList(new PumpTransfer(PumpType.IN, "Conduit", 560.0, "Test Transfer"))); + retList.put(Instant.parse("2025-10-02T00:00:00Z"), + Collections.singletonList(new PumpTransfer(PumpType.IN, "Canal", 750.0, "Test Transfer"))); + return retList; + } + + private enum TestDates + { + DEFAULT + { + @Override + public Instant startTime() { + this.startTime = Instant.parse("2025-08-30T00:00:00Z"); + return this.startTime; + } + + @Override + public Instant endTime() { + this.endTime = Instant.parse("2326-02-01T00:00:00Z"); + return this.endTime; + } + + @Override + public boolean startInclusive() { + this.startInclusive = true; + return this.startInclusive; + } + + @Override + public boolean endInclusive() { + this.endInclusive = true; + return this.endInclusive; + } + + @Override + public boolean isInDb() { + this.inDb = true; + return this.inDb; + } + }, + START_INCLUSIVE + { + @Override + public Instant startTime() { + this.startTime = Instant.parse("2025-08-30T00:00:00Z"); + return this.startTime; + } + + @Override + public Instant endTime() { + this.endTime = Instant.parse("2030-02-01T00:00:00Z"); + return this.endTime; + } + + @Override + public boolean startInclusive() { + this.startInclusive = true; + return this.startInclusive; + } + + @Override + public boolean endInclusive() { + this.endInclusive = true; + return this.endInclusive; + } + + @Override + public boolean isInDb() { + this.inDb = true; + return this.inDb; + } + }, + END_INCLUSIVE + { + @Override + public Instant startTime() { + this.startTime = Instant.parse("2025-08-30T00:00:00Z"); + return this.startTime; + } + + @Override + public Instant endTime() { + this.endTime = Instant.parse("2326-02-01T00:00:00Z"); + return this.endTime; + } + + @Override + public boolean startInclusive() { + this.startInclusive = true; + return this.startInclusive; + } + + @Override + public boolean endInclusive() { + this.endInclusive = true; + return this.endInclusive; + } + + @Override + public boolean isInDb() { + this.inDb = true; + return this.inDb; + } + }, + START_INCLUSIVE_FALSE + { + @Override + public Instant startTime() { + this.startTime = Instant.parse("2286-10-21T08:53:19Z"); + return this.startTime; + } + + @Override + public Instant endTime() { + this.endTime = Instant.parse("2326-02-01T00:00:00Z"); + return this.endTime; + } + + @Override + public boolean startInclusive() { + this.startInclusive = false; + return this.startInclusive; + } + + @Override + public boolean endInclusive() { + this.endInclusive = true; + return this.endInclusive; + } + + @Override + public boolean isInDb() { + this.inDb = false; + return this.inDb; + } + }, + END_INCLUSIVE_FALSE + { + @Override + public Instant startTime() { + this.startTime = Instant.parse("2025-08-30T00:00:00Z"); + return this.startTime; + } + + @Override + public Instant endTime() { + this.endTime = Instant.parse("2025-10-01T00:00:00Z"); + return this.endTime; + } + + @Override + public boolean startInclusive() { + this.startInclusive = true; + return this.startInclusive; + } + + @Override + public boolean endInclusive() { + this.endInclusive = false; + return this.endInclusive; + } + + @Override + public boolean isInDb() { + this.inDb = false; + return this.inDb; + } + }, + START_END_INCLUSIVE_FALSE + { + @Override + public Instant startTime() { + this.startTime = Instant.parse("2325-10-01T00:00:00Z"); + return this.startTime; + } + + @Override + public Instant endTime() { + this.endTime = Instant.parse("2326-02-01T00:00:00Z"); + return this.endTime; + } + + @Override + public boolean startInclusive() { + this.startInclusive = false; + return this.startInclusive; + } + + @Override + public boolean endInclusive() { + this.endInclusive = false; + return this.endInclusive; + } + + @Override + public boolean isInDb() { + this.inDb = false; + return this.inDb; + } + }; + + boolean inDb = true; + boolean startInclusive = true; + boolean endInclusive = true; + Instant startTime = Instant.now(); + Instant endTime = Instant.now(); + + public boolean isInDb() { + return this.inDb; + } + + public Instant startTime() { + return this.startTime; + } + + public Instant endTime() { + return this.endTime; + } + + public boolean startInclusive() { + return this.startInclusive; + } + + public boolean endInclusive() { + return this.endInclusive; + } + + } +} diff --git a/cwms-data-api/src/test/java/cwms/cda/helpers/DTOMatch.java b/cwms-data-api/src/test/java/cwms/cda/helpers/DTOMatch.java index 9d7ccff38..f6e167604 100644 --- a/cwms-data-api/src/test/java/cwms/cda/helpers/DTOMatch.java +++ b/cwms-data-api/src/test/java/cwms/cda/helpers/DTOMatch.java @@ -393,23 +393,29 @@ public static void assertMatch(Map> first, Map { - if (first != null && second != null) { + if (first != null && second != null && first.getPumpOut() != null && second.getPumpOut() != null) { assertMatch(first.getPumpOut(), second.getPumpOut()); - } else if (!(first == null && second == null)) { + } else if (!(first == null && second == null) + && !((first != null && first.getPumpOut() == null) + && (second != null && second.getPumpOut() == null))) { fail("Pump out locations do not match"); } }, () -> { - if (first != null && second != null) { + if (first != null && second != null && first.getPumpIn() != null && second.getPumpIn() != null) { assertMatch(first.getPumpIn(), second.getPumpIn()); - } else if (!(first == null && second == null)) { + } else if (!(first == null && second == null) + && !((first != null && first.getPumpIn() == null) + && (second != null && second.getPumpIn() == null))) { fail("Pump in locations do not match"); } }, () -> { - if (first != null && second != null) { + if (first != null && second != null && first.getPumpBelow() != null && second.getPumpBelow() != null) { assertMatch(first.getPumpBelow(), second.getPumpBelow()); - } else if (!(first == null && second == null)) { + } else if (!(first == null && second == null) + && !((first != null && first.getPumpBelow() == null) + && (second != null && second.getPumpBelow() == null))) { fail("Pump below locations do not match"); } }