From 6bba589116e8545d26e020ebe4c5d659ea9cc8de Mon Sep 17 00:00:00 2001 From: Tomek Date: Thu, 13 Jun 2024 14:48:59 +0200 Subject: [PATCH] OAM-191: adding unit of orderable id to identifiers (#54) * OAM-191: Changed orderable and lot identifying to contain also unit of orderable uuid. * OAM-91: Merged two interfaces. * OAM-91: Corrections made. * OAM-191: Corrections made. * OAM-191: Corrections made. * OAM-191: Added index migrations for StockCard identity change * OAM-191: Changed from OrderableLotUnit to OrderableLot. * OAM-191: Fixed OrderableLotIdentity * OAM-191: Corrected name. * OAM-191: Fixed issues. * OAM-191: Added running integration tests on build. * OAM-191: Reverted lockjson changes. * OAM-191: Added new integration test. * Revert "OAM-191: Added new integration test." This reverts commit 7990c1f5d42ae5df23648ba0cdcb4972a4136efd. * OAM-191: Added fix. * OAM-191: Added coverage tests. * OAM-191: Added coverage tests by moving test class form integration tests to unit tests. * OAM-191: Optimized imports. * OAM-191: Fixed test. * OAM-191: Addded coverage tests and renamed test class. --------- Co-authored-by: Piotr Wargulak --- build.sh | 2 +- .../StockCardRepositoryIntegrationTest.java | 19 ++ .../domain/card/StockCard.java | 8 +- .../domain/event/StockEventLineItem.java | 4 +- ...va => IdentifiableByOrderableLotUnit.java} | 5 +- ...ity.java => OrderableLotUnitIdentity.java} | 34 ++- .../PhysicalInventoryLineItem.java | 4 +- .../dto/PhysicalInventoryLineItemDto.java | 4 +- .../stockmanagement/dto/StockCardDto.java | 4 +- .../dto/StockEventLineItemDto.java | 4 +- .../repository/StockCardRepository.java | 7 +- .../service/PhysicalInventoryService.java | 8 +- .../service/StockCardBaseService.java | 1 + .../service/StockCardService.java | 6 +- .../service/StockCardSummariesService.java | 103 ++++--- .../StockEventNotificationProcessor.java | 4 +- .../StockEventProcessContextBuilder.java | 6 +- .../service/StockEventValidationsService.java | 6 +- .../util/StockEventProcessContext.java | 6 +- .../validators/ActiveStockCardsValidator.java | 6 +- ...OrderableLotUnitDuplicationValidator.java} | 14 +- .../validators/QuantityValidator.java | 6 +- ...8__added_stock_card_indexes_with_units.sql | 21 ++ .../StockCardSummariesServiceTest.java | 255 +++++++++++++++--- .../StockEventNotificationProcessorTest.java | 31 ++- .../StockEventValidationsServiceTest.java} | 55 ++-- .../util/OrderableLotIdentityTest.java | 45 ---- .../util/OrderableLotUnitIdentityTest.java | 162 +++++++++++ .../ActiveStockCardsValidatorTest.java | 8 +- ...rableLotUnitDuplicationValidatorTest.java} | 33 ++- 30 files changed, 647 insertions(+), 224 deletions(-) rename src/main/java/org/openlmis/stockmanagement/domain/identity/{IdentifiableByOrderableLot.java => IdentifiableByOrderableLotUnit.java} (92%) rename src/main/java/org/openlmis/stockmanagement/domain/identity/{OrderableLotIdentity.java => OrderableLotUnitIdentity.java} (58%) rename src/main/java/org/openlmis/stockmanagement/validators/{OrderableLotDuplicationValidator.java => OrderableLotUnitDuplicationValidator.java} (83%) create mode 100644 src/main/resources/db/migration/20240612140304888__added_stock_card_indexes_with_units.sql rename src/{integration-test/java/org/openlmis/stockmanagement/service/StockEventValidationsServiceIntegrationTest.java => test/java/org/openlmis/stockmanagement/service/StockEventValidationsServiceTest.java} (88%) delete mode 100644 src/test/java/org/openlmis/stockmanagement/util/OrderableLotIdentityTest.java create mode 100644 src/test/java/org/openlmis/stockmanagement/util/OrderableLotUnitIdentityTest.java rename src/test/java/org/openlmis/stockmanagement/validators/{OrderableLotDuplicationValidatorTest.java => OrderableLotUnitDuplicationValidatorTest.java} (75%) diff --git a/build.sh b/build.sh index 7661734a..bd51e706 100755 --- a/build.sh +++ b/build.sh @@ -7,4 +7,4 @@ --source-file src/main/resources/messages_en.properties # Run Gradle build -gradle clean build +gradle clean build integrationTest diff --git a/src/integration-test/java/org/openlmis/stockmanagement/repository/StockCardRepositoryIntegrationTest.java b/src/integration-test/java/org/openlmis/stockmanagement/repository/StockCardRepositoryIntegrationTest.java index 7da6c870..3ffde720 100644 --- a/src/integration-test/java/org/openlmis/stockmanagement/repository/StockCardRepositoryIntegrationTest.java +++ b/src/integration-test/java/org/openlmis/stockmanagement/repository/StockCardRepositoryIntegrationTest.java @@ -78,6 +78,11 @@ StockCard generateInstance() throws Exception { } private StockCard generateInstance(UUID facility, UUID program, UUID product, UUID lot) { + return generateInstance(facility, program, product, lot, null); + } + + private StockCard generateInstance(UUID facility, UUID program, UUID product, UUID lot, + UUID unitId) { StockEvent event = new StockEventDataBuilder() .withoutId() .withFacility(facility) @@ -96,6 +101,7 @@ private StockCard generateInstance(UUID facility, UUID program, UUID product, UU .withoutId() .withOrderableId(product) .withLotId(lot) + .withUnitOfOrderableId(unitId) .withLineItem(lineItem) .withIsActive(true) .build(); @@ -104,6 +110,19 @@ private StockCard generateInstance(UUID facility, UUID program, UUID product, UU return stockCard; } + @Test(expected = PersistenceException.class) + public void shouldNotAllowCreatingCardForTheSameFacilityProgramProductAndLotAndUnit() + throws Exception { + stockCard1 = generateInstance(); + stockCard2 = generateInstance(stockCard1.getFacilityId(), stockCard1.getProgramId(), + stockCard1.getOrderableId(), stockCard1.getLotId(), stockCard1.getUnitOfOrderableId()); + + stockCardRepository.save(stockCard1); + stockCardRepository.save(stockCard2); + + entityManager.flush(); + } + @Test(expected = PersistenceException.class) public void shouldNotAllowCreatingCardForTheSameFacilityProgramProductAndLot() throws Exception { stockCard1 = generateInstance(); diff --git a/src/main/java/org/openlmis/stockmanagement/domain/card/StockCard.java b/src/main/java/org/openlmis/stockmanagement/domain/card/StockCard.java index 9f251676..ab7eab29 100644 --- a/src/main/java/org/openlmis/stockmanagement/domain/card/StockCard.java +++ b/src/main/java/org/openlmis/stockmanagement/domain/card/StockCard.java @@ -47,7 +47,7 @@ import org.hibernate.annotations.LazyCollection; import org.openlmis.stockmanagement.domain.BaseEntity; import org.openlmis.stockmanagement.domain.event.StockEvent; -import org.openlmis.stockmanagement.domain.identity.IdentifiableByOrderableLot; +import org.openlmis.stockmanagement.domain.identity.IdentifiableByOrderableLotUnit; import org.openlmis.stockmanagement.dto.StockEventDto; import org.openlmis.stockmanagement.dto.StockEventLineItemDto; import org.openlmis.stockmanagement.exception.ValidationMessageException; @@ -56,8 +56,8 @@ import org.slf4j.LoggerFactory; @NamedQuery(name = StockCard.QUERY_FIND_LOT_IDENT_BY_PROG_FACILITY, - query = "SELECT new org.openlmis.stockmanagement.domain.identity.OrderableLotIdentity(" - + "s.orderableId, s.lotId)" + query = "SELECT new org.openlmis.stockmanagement.domain.identity.OrderableLotUnitIdentity(" + + "s.orderableId, s.lotId, s.unitOfOrderableId)" + " FROM StockCard s" + " WHERE s.programId = :" + StockCard.PARAM_PROGRAM_ID + " AND s.facilityId = :" + StockCard.PARAM_FACILITY_ID) @@ -70,7 +70,7 @@ indexes = @Index(columnList = "facilityId,programId,orderableId")) //the above line creates an index, it'll make select statements faster //especially for getStockCardIdBy method of StockCardRepository -public class StockCard extends BaseEntity implements IdentifiableByOrderableLot { +public class StockCard extends BaseEntity implements IdentifiableByOrderableLotUnit { public static final String QUERY_FIND_LOT_IDENT_BY_PROG_FACILITY = "StockCard" + ".findLotIdentByProgFacility"; public static final String PARAM_PROGRAM_ID = "programId"; diff --git a/src/main/java/org/openlmis/stockmanagement/domain/event/StockEventLineItem.java b/src/main/java/org/openlmis/stockmanagement/domain/event/StockEventLineItem.java index 9fb069fd..5e139731 100644 --- a/src/main/java/org/openlmis/stockmanagement/domain/event/StockEventLineItem.java +++ b/src/main/java/org/openlmis/stockmanagement/domain/event/StockEventLineItem.java @@ -37,7 +37,7 @@ import org.openlmis.stockmanagement.domain.BaseEntity; import org.openlmis.stockmanagement.domain.ExtraDataConverter; import org.openlmis.stockmanagement.domain.common.VvmApplicable; -import org.openlmis.stockmanagement.domain.identity.IdentifiableByOrderableLot; +import org.openlmis.stockmanagement.domain.identity.IdentifiableByOrderableLotUnit; import org.openlmis.stockmanagement.domain.physicalinventory.PhysicalInventoryLineItemAdjustment; @Data @@ -47,7 +47,7 @@ @Entity @Table(name = "stock_event_line_items", schema = "stockmanagement") public class StockEventLineItem extends BaseEntity - implements IdentifiableByOrderableLot, VvmApplicable { + implements IdentifiableByOrderableLotUnit, VvmApplicable { @Column(nullable = false) private UUID orderableId; diff --git a/src/main/java/org/openlmis/stockmanagement/domain/identity/IdentifiableByOrderableLot.java b/src/main/java/org/openlmis/stockmanagement/domain/identity/IdentifiableByOrderableLotUnit.java similarity index 92% rename from src/main/java/org/openlmis/stockmanagement/domain/identity/IdentifiableByOrderableLot.java rename to src/main/java/org/openlmis/stockmanagement/domain/identity/IdentifiableByOrderableLotUnit.java index 13cdd67b..525fbf69 100644 --- a/src/main/java/org/openlmis/stockmanagement/domain/identity/IdentifiableByOrderableLot.java +++ b/src/main/java/org/openlmis/stockmanagement/domain/identity/IdentifiableByOrderableLotUnit.java @@ -17,8 +17,11 @@ import java.util.UUID; -public interface IdentifiableByOrderableLot { +public interface IdentifiableByOrderableLotUnit { + UUID getOrderableId(); UUID getLotId(); + + UUID getUnitOfOrderableId(); } diff --git a/src/main/java/org/openlmis/stockmanagement/domain/identity/OrderableLotIdentity.java b/src/main/java/org/openlmis/stockmanagement/domain/identity/OrderableLotUnitIdentity.java similarity index 58% rename from src/main/java/org/openlmis/stockmanagement/domain/identity/OrderableLotIdentity.java rename to src/main/java/org/openlmis/stockmanagement/domain/identity/OrderableLotUnitIdentity.java index 771851b2..009ec6b8 100644 --- a/src/main/java/org/openlmis/stockmanagement/domain/identity/OrderableLotIdentity.java +++ b/src/main/java/org/openlmis/stockmanagement/domain/identity/OrderableLotUnitIdentity.java @@ -19,15 +19,32 @@ import lombok.Getter; import lombok.Setter; +/** + * Class acting as identity where triple (orderableId, lotId, unitOfOrderableId) is unique. + */ @Getter @Setter -public class OrderableLotIdentity { +public class OrderableLotUnitIdentity { private UUID orderableId; private UUID lotId; + private UUID unitOfOrderableId; - public OrderableLotIdentity(UUID orderableId, UUID lotId) { + /** + * Constructor for OrderableLotUnitIdentity. + * + * @param orderableId orderable id + * @param lotId lot id + * @param unitOfOrderableId unit of orderable id + */ + public OrderableLotUnitIdentity(UUID orderableId, UUID lotId, UUID unitOfOrderableId) { this.orderableId = orderableId; this.lotId = lotId; + this.unitOfOrderableId = unitOfOrderableId; + } + + public static OrderableLotUnitIdentity identityOf(IdentifiableByOrderableLotUnit identifiable) { + return new OrderableLotUnitIdentity(identifiable.getOrderableId(), identifiable.getLotId(), + identifiable.getUnitOfOrderableId()); } @Override @@ -39,22 +56,23 @@ public boolean equals(Object object) { return false; } - OrderableLotIdentity that = (OrderableLotIdentity) object; + OrderableLotUnitIdentity that = (OrderableLotUnitIdentity) object; if (orderableId != null ? !orderableId.equals(that.orderableId) : that.orderableId != null) { return false; } - return lotId != null ? lotId.equals(that.lotId) : that.lotId == null; + if (lotId != null ? !lotId.equals(that.lotId) : that.lotId != null) { + return false; + } + return unitOfOrderableId == null ? that.unitOfOrderableId == null : + unitOfOrderableId.equals(that.unitOfOrderableId); } @Override public int hashCode() { int result = orderableId != null ? orderableId.hashCode() : 0; result = 31 * result + (lotId != null ? lotId.hashCode() : 0); + result = 31 * result + (unitOfOrderableId != null ? unitOfOrderableId.hashCode() : 0); return result; } - - public static OrderableLotIdentity identityOf(IdentifiableByOrderableLot identifiable) { - return new OrderableLotIdentity(identifiable.getOrderableId(), identifiable.getLotId()); - } } diff --git a/src/main/java/org/openlmis/stockmanagement/domain/physicalinventory/PhysicalInventoryLineItem.java b/src/main/java/org/openlmis/stockmanagement/domain/physicalinventory/PhysicalInventoryLineItem.java index ff4675d0..d7ea5c75 100644 --- a/src/main/java/org/openlmis/stockmanagement/domain/physicalinventory/PhysicalInventoryLineItem.java +++ b/src/main/java/org/openlmis/stockmanagement/domain/physicalinventory/PhysicalInventoryLineItem.java @@ -35,7 +35,7 @@ import org.openlmis.stockmanagement.domain.BaseEntity; import org.openlmis.stockmanagement.domain.ExtraDataConverter; import org.openlmis.stockmanagement.domain.common.VvmApplicable; -import org.openlmis.stockmanagement.domain.identity.IdentifiableByOrderableLot; +import org.openlmis.stockmanagement.domain.identity.IdentifiableByOrderableLotUnit; @Entity @Data @@ -45,7 +45,7 @@ @Table(name = "physical_inventory_line_items", schema = "stockmanagement") public class PhysicalInventoryLineItem extends BaseEntity - implements VvmApplicable, IdentifiableByOrderableLot { + implements VvmApplicable, IdentifiableByOrderableLotUnit { @Column(nullable = false) private UUID orderableId; @Column diff --git a/src/main/java/org/openlmis/stockmanagement/dto/PhysicalInventoryLineItemDto.java b/src/main/java/org/openlmis/stockmanagement/dto/PhysicalInventoryLineItemDto.java index 4ecf757d..a01b6069 100644 --- a/src/main/java/org/openlmis/stockmanagement/dto/PhysicalInventoryLineItemDto.java +++ b/src/main/java/org/openlmis/stockmanagement/dto/PhysicalInventoryLineItemDto.java @@ -24,7 +24,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import org.openlmis.stockmanagement.domain.common.VvmApplicable; -import org.openlmis.stockmanagement.domain.identity.IdentifiableByOrderableLot; +import org.openlmis.stockmanagement.domain.identity.IdentifiableByOrderableLotUnit; import org.openlmis.stockmanagement.domain.physicalinventory.PhysicalInventory; import org.openlmis.stockmanagement.domain.physicalinventory.PhysicalInventoryLineItem; import org.openlmis.stockmanagement.domain.physicalinventory.PhysicalInventoryLineItemAdjustment; @@ -33,7 +33,7 @@ @NoArgsConstructor @AllArgsConstructor @Builder -public class PhysicalInventoryLineItemDto implements IdentifiableByOrderableLot, VvmApplicable { +public class PhysicalInventoryLineItemDto implements IdentifiableByOrderableLotUnit, VvmApplicable { private UUID orderableId; private UUID lotId; private UUID unitOfOrderableId; diff --git a/src/main/java/org/openlmis/stockmanagement/dto/StockCardDto.java b/src/main/java/org/openlmis/stockmanagement/dto/StockCardDto.java index 61a7b607..85142515 100644 --- a/src/main/java/org/openlmis/stockmanagement/dto/StockCardDto.java +++ b/src/main/java/org/openlmis/stockmanagement/dto/StockCardDto.java @@ -34,7 +34,7 @@ import lombok.Setter; import lombok.ToString; import org.openlmis.stockmanagement.domain.card.StockCard; -import org.openlmis.stockmanagement.domain.identity.IdentifiableByOrderableLot; +import org.openlmis.stockmanagement.domain.identity.IdentifiableByOrderableLotUnit; import org.openlmis.stockmanagement.dto.referencedata.FacilityDto; import org.openlmis.stockmanagement.dto.referencedata.LotDto; import org.openlmis.stockmanagement.dto.referencedata.OrderableDto; @@ -47,7 +47,7 @@ @Setter @ToString @EqualsAndHashCode -public final class StockCardDto implements IdentifiableByOrderableLot { +public final class StockCardDto implements IdentifiableByOrderableLotUnit { @JsonInclude(NON_NULL) private UUID id; diff --git a/src/main/java/org/openlmis/stockmanagement/dto/StockEventLineItemDto.java b/src/main/java/org/openlmis/stockmanagement/dto/StockEventLineItemDto.java index 2c020f32..0b41e6c4 100644 --- a/src/main/java/org/openlmis/stockmanagement/dto/StockEventLineItemDto.java +++ b/src/main/java/org/openlmis/stockmanagement/dto/StockEventLineItemDto.java @@ -30,14 +30,14 @@ import lombok.Setter; import org.openlmis.stockmanagement.domain.common.VvmApplicable; import org.openlmis.stockmanagement.domain.event.StockEventLineItem; -import org.openlmis.stockmanagement.domain.identity.IdentifiableByOrderableLot; +import org.openlmis.stockmanagement.domain.identity.IdentifiableByOrderableLotUnit; import org.openlmis.stockmanagement.domain.physicalinventory.PhysicalInventoryLineItemAdjustment; @Setter @Getter @AllArgsConstructor @NoArgsConstructor -public class StockEventLineItemDto implements IdentifiableByOrderableLot, VvmApplicable { +public class StockEventLineItemDto implements IdentifiableByOrderableLotUnit, VvmApplicable { private UUID orderableId; private UUID lotId; private Integer quantity; diff --git a/src/main/java/org/openlmis/stockmanagement/repository/StockCardRepository.java b/src/main/java/org/openlmis/stockmanagement/repository/StockCardRepository.java index 968fc92e..c1e38064 100644 --- a/src/main/java/org/openlmis/stockmanagement/repository/StockCardRepository.java +++ b/src/main/java/org/openlmis/stockmanagement/repository/StockCardRepository.java @@ -20,7 +20,7 @@ import java.util.UUID; import org.openlmis.stockmanagement.domain.card.StockCard; import org.openlmis.stockmanagement.domain.event.StockEvent; -import org.openlmis.stockmanagement.domain.identity.OrderableLotIdentity; +import org.openlmis.stockmanagement.domain.identity.OrderableLotUnitIdentity; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; @@ -50,8 +50,9 @@ List findByOrderableIdInAndProgramIdAndFacilityId( StockCard findByOriginEvent(@Param("originEventId") StockEvent stockEvent); @Query(name = StockCard.QUERY_FIND_LOT_IDENT_BY_PROG_FACILITY) - List getIdentitiesBy(@Param(StockCard.PARAM_PROGRAM_ID) UUID programId, - @Param(StockCard.PARAM_FACILITY_ID) UUID facilityId); + List getIdentitiesBy(@Param(StockCard.PARAM_PROGRAM_ID) UUID programId, + @Param(StockCard.PARAM_FACILITY_ID) + UUID facilityId); Page findByFacilityIdInAndProgramIdInAndIdIn(Collection facilityIds, Collection programIds, Collection ids, Pageable pageable); diff --git a/src/main/java/org/openlmis/stockmanagement/service/PhysicalInventoryService.java b/src/main/java/org/openlmis/stockmanagement/service/PhysicalInventoryService.java index c27e5c3b..dac289c2 100644 --- a/src/main/java/org/openlmis/stockmanagement/service/PhysicalInventoryService.java +++ b/src/main/java/org/openlmis/stockmanagement/service/PhysicalInventoryService.java @@ -28,7 +28,7 @@ import java.util.UUID; import org.openlmis.stockmanagement.domain.card.StockCard; import org.openlmis.stockmanagement.domain.event.StockEvent; -import org.openlmis.stockmanagement.domain.identity.OrderableLotIdentity; +import org.openlmis.stockmanagement.domain.identity.OrderableLotUnitIdentity; import org.openlmis.stockmanagement.domain.physicalinventory.PhysicalInventory; import org.openlmis.stockmanagement.domain.physicalinventory.PhysicalInventoryLineItem; import org.openlmis.stockmanagement.dto.PhysicalInventoryDto; @@ -167,13 +167,13 @@ void submitPhysicalInventory(PhysicalInventoryDto inventoryDto, UUID eventId) { inventory.setStockEvent(event); } - Map cards = calculatedStockOnHandService + Map cards = calculatedStockOnHandService .getStockCardsWithStockOnHand(inventory.getProgramId(), inventory.getFacilityId()) .stream() - .collect(toMap(OrderableLotIdentity::identityOf, card -> card)); + .collect(toMap(OrderableLotUnitIdentity::identityOf, card -> card)); for (PhysicalInventoryLineItem line : inventory.getLineItems()) { - StockCard stockCard = cards.get(OrderableLotIdentity.identityOf(line)); + StockCard stockCard = cards.get(OrderableLotUnitIdentity.identityOf(line)); if (stockCard != null) { line.setPreviousStockOnHandWhenSubmitted(stockCard.getStockOnHand()); } diff --git a/src/main/java/org/openlmis/stockmanagement/service/StockCardBaseService.java b/src/main/java/org/openlmis/stockmanagement/service/StockCardBaseService.java index bbc51c72..3bcfe655 100644 --- a/src/main/java/org/openlmis/stockmanagement/service/StockCardBaseService.java +++ b/src/main/java/org/openlmis/stockmanagement/service/StockCardBaseService.java @@ -85,6 +85,7 @@ private StockCardDto cardToDto(FacilityDto facility, ProgramDto program, if (card.getLotId() != null) { cardDto.setLot(LotDto.builder().id(card.getLotId()).build()); } + cardDto.setUnitOfOrderableId(card.getUnitOfOrderableId()); List lineItems = cardDto.getLineItems(); if (!isEmpty(lineItems)) { cardDto.setLastUpdate(card.getOccurredDate()); diff --git a/src/main/java/org/openlmis/stockmanagement/service/StockCardService.java b/src/main/java/org/openlmis/stockmanagement/service/StockCardService.java index af5e2880..2facbf33 100644 --- a/src/main/java/org/openlmis/stockmanagement/service/StockCardService.java +++ b/src/main/java/org/openlmis/stockmanagement/service/StockCardService.java @@ -20,7 +20,7 @@ import static org.apache.commons.collections4.CollectionUtils.isEmpty; import static org.openlmis.stockmanagement.domain.card.StockCard.createStockCardFrom; import static org.openlmis.stockmanagement.domain.card.StockCardLineItem.createLineItemFrom; -import static org.openlmis.stockmanagement.domain.identity.OrderableLotIdentity.identityOf; +import static org.openlmis.stockmanagement.domain.identity.OrderableLotUnitIdentity.identityOf; import static org.openlmis.stockmanagement.domain.reason.ReasonCategory.PHYSICAL_INVENTORY; import static org.openlmis.stockmanagement.service.PermissionService.STOCK_CARDS_VIEW; @@ -36,7 +36,7 @@ import javax.validation.constraints.NotNull; import org.openlmis.stockmanagement.domain.card.StockCard; import org.openlmis.stockmanagement.domain.card.StockCardLineItem; -import org.openlmis.stockmanagement.domain.identity.OrderableLotIdentity; +import org.openlmis.stockmanagement.domain.identity.OrderableLotUnitIdentity; import org.openlmis.stockmanagement.domain.sourcedestination.Node; import org.openlmis.stockmanagement.domain.sourcedestination.Organization; import org.openlmis.stockmanagement.dto.StockCardDto; @@ -238,7 +238,7 @@ private List getSavedButNewLineItems(List cardsToU private StockCard findOrCreateCard(StockEventDto eventDto, StockEventLineItemDto eventLineItem, UUID savedEventId, List cardsToUpdate) { - OrderableLotIdentity identity = identityOf(eventLineItem); + OrderableLotUnitIdentity identity = identityOf(eventLineItem); StockCard card = eventDto.getContext().findCard(identity); if (null == card) { diff --git a/src/main/java/org/openlmis/stockmanagement/service/StockCardSummariesService.java b/src/main/java/org/openlmis/stockmanagement/service/StockCardSummariesService.java index b53eb2ca..df868450 100644 --- a/src/main/java/org/openlmis/stockmanagement/service/StockCardSummariesService.java +++ b/src/main/java/org/openlmis/stockmanagement/service/StockCardSummariesService.java @@ -22,7 +22,6 @@ import static java.util.stream.Stream.concat; import static java.util.stream.Stream.empty; import static org.apache.commons.collections.CollectionUtils.isEmpty; -import static org.openlmis.stockmanagement.domain.identity.OrderableLotIdentity.identityOf; import java.time.LocalDate; import java.util.ArrayList; @@ -34,14 +33,14 @@ import java.util.Set; import java.util.UUID; import java.util.stream.Stream; - +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; import lombok.Getter; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import org.openlmis.stockmanagement.domain.card.StockCard; import org.openlmis.stockmanagement.domain.event.CalculatedStockOnHand; -import org.openlmis.stockmanagement.domain.identity.IdentifiableByOrderableLot; -import org.openlmis.stockmanagement.domain.identity.OrderableLotIdentity; +import org.openlmis.stockmanagement.domain.identity.OrderableLotUnitIdentity; import org.openlmis.stockmanagement.dto.StockCardDto; import org.openlmis.stockmanagement.dto.referencedata.LotDto; import org.openlmis.stockmanagement.dto.referencedata.OrderableDto; @@ -106,7 +105,9 @@ public class StockCardSummariesService extends StockCardBaseService { * @return map of stock cards assigned to orderable ids */ public Map getGroupedStockCards(UUID programId, UUID facilityId, - Set orderableIds, LocalDate startDate, LocalDate endDate) { + Set orderableIds, + LocalDate startDate, + LocalDate endDate) { List stockCards = calculatedStockOnHandService .getStockCardsWithStockOnHand(programId, facilityId); @@ -184,8 +185,8 @@ public StockCardSummaries findStockCards(StockCardSummariesV2SearchParams params // FIXME: Fix page retrieving/calculation, // page size may be wrong when there are orderables matching not only by lot codes List stockCards = calculatedStockOnHandService.getStockCardsWithStockOnHand( - params.getProgramId(), params.getFacilityId(), params.getAsOfDate(), - orderableIdsForStockCard, lotCodeIds); + params.getProgramId(), params.getFacilityId(), params.getAsOfDate(), + orderableIdsForStockCard, lotCodeIds); Page orderablesPage = approvedProducts.getOrderablesPage(); StockCardSummaries result = new StockCardSummaries( @@ -233,34 +234,38 @@ public Page findStockCards(UUID programId, UUID facilityId, Pageab */ public List createDummyStockCards(UUID programId, UUID facilityId) { //this will not read the whole table, only the orderable id and lot id - List existingCardIdentities = + List existingCardIdentities = stockCardRepository.getIdentitiesBy(programId, facilityId); LOGGER.info("Calling ref data to get all approved orderables"); - Map orderableLotsMap = createOrderableLots( + Map orderableLotMap = createOrderableLotMap( orderableReferenceDataService.findAll()); //create dummy(fake/not persisted) cards for approved orderables that don't have cards yet - List dummyCards = createDummyCards(programId, facilityId, orderableLotsMap.values(), + List dummyCards = createDummyCards(programId, facilityId, + orderableLotMap.values(), existingCardIdentities).collect(toList()); - return assignOrderableLotRemoveLineItems(createDtos(dummyCards), orderableLotsMap); + return loadOrderableLotUnitAndRemoveLineItems(createDtos(dummyCards), orderableLotMap); } private List cardsToDtos(List cards) { LOGGER.info("Calling ref data to get all approved orderables"); - Map orderableLotsMap = createOrderableLots( + Map orderableLotsMap = createOrderableLotMap( orderableReferenceDataService.findAll()); - return assignOrderableLotRemoveLineItems(createDtos(cards), orderableLotsMap); + return loadOrderableLotUnitAndRemoveLineItems(createDtos(cards), orderableLotsMap); } - private List assignOrderableLotRemoveLineItems( + private List loadOrderableLotUnitAndRemoveLineItems( List stockCardDtos, - Map orderableLotsMap) { + Map orderableLotMap) { stockCardDtos.forEach(stockCardDto -> { - OrderableLot orderableLot = orderableLotsMap.get(identityOf(stockCardDto)); - stockCardDto.setOrderable(orderableLot.getOrderable()); - stockCardDto.setLot(orderableLot.getLot()); + OrderableLot orderableLot = + orderableLotMap.get(OrderableLotIdentity.identityOf(stockCardDto)); + if (orderableLot != null) { + stockCardDto.setOrderable(orderableLot.getOrderable()); + stockCardDto.setLot(orderableLot.getLot()); + } stockCardDto.setLineItems(null);//line items are not needed in summary }); return stockCardDtos; @@ -268,29 +273,30 @@ private List assignOrderableLotRemoveLineItems( private Stream createDummyCards(UUID programId, UUID facilityId, Collection orderableLots, - List cardIdentities) { - return filterOrderableLotsWithoutCards(orderableLots, cardIdentities) + List cardIdentities) { + return filterOrderableLotUnitsWithoutCards(orderableLots, cardIdentities) .stream() .map(orderableLot -> StockCard.builder() .programId(programId) .facilityId(facilityId) - .orderableId(orderableLot.getOrderable().getId()) + .orderableId(orderableLot.getOrderableId()) .lotId(orderableLot.getLotId()) .lineItems(emptyList())//dummy cards don't have line items .build()); } - private List filterOrderableLotsWithoutCards( - Collection orderableLots, List cardIdentities) { - return orderableLots.stream() - .filter(orderableLot -> cardIdentities.stream() - .noneMatch(cardIdentity -> cardIdentity.equals(identityOf(orderableLot)))) - .collect(toList()); + private List filterOrderableLotUnitsWithoutCards( + Collection orderableLots, List cardIdentities) { + return orderableLots.stream().filter(orderableLot -> cardIdentities.stream().noneMatch( + cardIdentity -> OrderableLotIdentity.identityOf(orderableLot) + .equalsOrderableLotUnitIdentity(cardIdentity))).collect(toList()); } - private Map createOrderableLots( + private Map createOrderableLotMap( List orderableDtos) { - Stream orderableLots = orderableDtos.stream().flatMap(this::lotsOfOrderable); + Stream orderableLots = orderableDtos + .stream() + .flatMap(this::lotsOfOrderable); Stream orderablesOnly = orderableDtos.stream() .map(orderableDto -> new OrderableLot(orderableDto, null)); @@ -357,23 +363,48 @@ private ImmutablePair assignOrderableToStockCard( new StockCardAggregate(stockCards, calculatedStockOnHands)); } + @AllArgsConstructor @Getter - private static class OrderableLot implements IdentifiableByOrderableLot { + private static class OrderableLot { + private OrderableDto orderable; private LotDto lot; - OrderableLot(OrderableDto orderable, LotDto lot) { - this.orderable = orderable; - this.lot = lot; - } - public UUID getLotId() { return lot == null ? null : lot.getId(); } public UUID getOrderableId() { - return orderable.getId(); + return orderable == null ? null : orderable.getId(); } } + @AllArgsConstructor + @Getter + @EqualsAndHashCode + static class OrderableLotIdentity { + + private UUID orderableId; + private UUID lotId; + + static OrderableLotIdentity identityOf(OrderableLot orderableLot) { + return new OrderableLotIdentity(orderableLot.getOrderableId(), orderableLot.getLotId()); + } + + static OrderableLotIdentity identityOf(StockCardDto stockCardDto) { + return new OrderableLotIdentity(stockCardDto.getOrderableId(), stockCardDto.getLotId()); + } + + boolean equalsOrderableLotUnitIdentity(OrderableLotUnitIdentity identity) { + if (identity == null) { + return false; + } + + if (orderableId != null ? !orderableId.equals(identity.getOrderableId()) : + identity.getOrderableId() != null) { + return false; + } + return lotId == null ? identity.getLotId() == null : lotId.equals(identity.getLotId()); + } + } } diff --git a/src/main/java/org/openlmis/stockmanagement/service/StockEventNotificationProcessor.java b/src/main/java/org/openlmis/stockmanagement/service/StockEventNotificationProcessor.java index 2f3ca3cb..9a2b2753 100644 --- a/src/main/java/org/openlmis/stockmanagement/service/StockEventNotificationProcessor.java +++ b/src/main/java/org/openlmis/stockmanagement/service/StockEventNotificationProcessor.java @@ -19,7 +19,7 @@ import java.util.UUID; import org.openlmis.stockmanagement.domain.card.StockCard; -import org.openlmis.stockmanagement.domain.identity.OrderableLotIdentity; +import org.openlmis.stockmanagement.domain.identity.OrderableLotUnitIdentity; import org.openlmis.stockmanagement.dto.StockEventDto; import org.openlmis.stockmanagement.dto.StockEventLineItemDto; import org.openlmis.stockmanagement.dto.referencedata.RightDto; @@ -66,7 +66,7 @@ private void callNotifications(StockEventDto event, StockEventLineItemDto eventL profiler.setLogger(XLOGGER); profiler.start("COPY_STOCK_CARD"); - OrderableLotIdentity identity = OrderableLotIdentity.identityOf(eventLine); + OrderableLotUnitIdentity identity = OrderableLotUnitIdentity.identityOf(eventLine); StockCard stockCard = event.getContext().findCard(identity); if (stockCard.getStockOnHand() == 0) { diff --git a/src/main/java/org/openlmis/stockmanagement/service/StockEventProcessContextBuilder.java b/src/main/java/org/openlmis/stockmanagement/service/StockEventProcessContextBuilder.java index 5e16f95f..4373014a 100644 --- a/src/main/java/org/openlmis/stockmanagement/service/StockEventProcessContextBuilder.java +++ b/src/main/java/org/openlmis/stockmanagement/service/StockEventProcessContextBuilder.java @@ -29,7 +29,7 @@ import lombok.NoArgsConstructor; import org.openlmis.stockmanagement.domain.card.StockCard; import org.openlmis.stockmanagement.domain.card.StockCardLineItem; -import org.openlmis.stockmanagement.domain.identity.OrderableLotIdentity; +import org.openlmis.stockmanagement.domain.identity.OrderableLotUnitIdentity; import org.openlmis.stockmanagement.domain.reason.StockCardLineItemReason; import org.openlmis.stockmanagement.domain.sourcedestination.Node; import org.openlmis.stockmanagement.domain.sourcedestination.ValidDestinationAssignment; @@ -199,8 +199,8 @@ public StockEventProcessContext buildContext(StockEventDto eventDto) { .getStockCardsWithStockOnHandByOrderableIds(eventDto.getProgramId(), eventDto.getFacilityId(), orderableIds); LazyList cards = new LazyList<>(cardsSupplier); - LazyGrouping cardsGroupedByIdentity = new LazyGrouping<>( - cards, OrderableLotIdentity::identityOf + LazyGrouping cardsGroupedByIdentity = new LazyGrouping<>( + cards, OrderableLotUnitIdentity::identityOf ); context.setCards(cardsGroupedByIdentity); diff --git a/src/main/java/org/openlmis/stockmanagement/service/StockEventValidationsService.java b/src/main/java/org/openlmis/stockmanagement/service/StockEventValidationsService.java index a8b535fa..24cef6f3 100644 --- a/src/main/java/org/openlmis/stockmanagement/service/StockEventValidationsService.java +++ b/src/main/java/org/openlmis/stockmanagement/service/StockEventValidationsService.java @@ -24,7 +24,7 @@ import org.openlmis.stockmanagement.validators.ApprovedOrderableValidator; import org.openlmis.stockmanagement.validators.LotValidator; import org.openlmis.stockmanagement.validators.MandatoryFieldsValidator; -import org.openlmis.stockmanagement.validators.OrderableLotDuplicationValidator; +import org.openlmis.stockmanagement.validators.OrderableLotUnitDuplicationValidator; import org.openlmis.stockmanagement.validators.PhysicalInventoryAdjustmentReasonsValidator; import org.openlmis.stockmanagement.validators.QuantityValidator; import org.openlmis.stockmanagement.validators.ReasonExistenceValidator; @@ -54,7 +54,7 @@ public class StockEventValidationsService { private MandatoryFieldsValidator mandatoryFieldsValidator; @Autowired - private OrderableLotDuplicationValidator orderableLotDuplicationValidator; + private OrderableLotUnitDuplicationValidator orderableLotUnitDuplicationValidator; @Autowired private PhysicalInventoryAdjustmentReasonsValidator physicalInventoryAdjustmentReasonsValidator; @@ -92,7 +92,7 @@ public void validate(StockEventDto stockEventDto) { approvedOrderableValidator.validate(stockEventDto); lotValidator.validate(stockEventDto); mandatoryFieldsValidator.validate(stockEventDto); - orderableLotDuplicationValidator.validate(stockEventDto); + orderableLotUnitDuplicationValidator.validate(stockEventDto); physicalInventoryAdjustmentReasonsValidator.validate(stockEventDto); quantityValidator.validate(stockEventDto); existenceValidator.validate(stockEventDto); diff --git a/src/main/java/org/openlmis/stockmanagement/util/StockEventProcessContext.java b/src/main/java/org/openlmis/stockmanagement/util/StockEventProcessContext.java index 860b3988..d25358bc 100644 --- a/src/main/java/org/openlmis/stockmanagement/util/StockEventProcessContext.java +++ b/src/main/java/org/openlmis/stockmanagement/util/StockEventProcessContext.java @@ -21,7 +21,7 @@ import lombok.Getter; import lombok.Setter; import org.openlmis.stockmanagement.domain.card.StockCard; -import org.openlmis.stockmanagement.domain.identity.OrderableLotIdentity; +import org.openlmis.stockmanagement.domain.identity.OrderableLotUnitIdentity; import org.openlmis.stockmanagement.domain.reason.StockCardLineItemReason; import org.openlmis.stockmanagement.domain.sourcedestination.Node; import org.openlmis.stockmanagement.domain.sourcedestination.ValidDestinationAssignment; @@ -52,7 +52,7 @@ public class StockEventProcessContext { private LazyGrouping cardReasons; private LazyGrouping eventReasons; private LazyGrouping nodes; - private LazyGrouping cards; + private LazyGrouping cards; @Getter private UUID unpackReasonId; @@ -102,7 +102,7 @@ public Node findNode(UUID nodeId) { return nodes.get().get(nodeId); } - public StockCard findCard(OrderableLotIdentity identity) { + public StockCard findCard(OrderableLotUnitIdentity identity) { return cards.get().get(identity); } diff --git a/src/main/java/org/openlmis/stockmanagement/validators/ActiveStockCardsValidator.java b/src/main/java/org/openlmis/stockmanagement/validators/ActiveStockCardsValidator.java index 833affb6..ac26bb03 100644 --- a/src/main/java/org/openlmis/stockmanagement/validators/ActiveStockCardsValidator.java +++ b/src/main/java/org/openlmis/stockmanagement/validators/ActiveStockCardsValidator.java @@ -19,7 +19,7 @@ import static org.openlmis.stockmanagement.i18n.MessageKeys.ERROR_PHYSICAL_INVENTORY_NOT_INCLUDE_ACTIVE_STOCK_CARD; import java.util.List; -import org.openlmis.stockmanagement.domain.identity.OrderableLotIdentity; +import org.openlmis.stockmanagement.domain.identity.OrderableLotUnitIdentity; import org.openlmis.stockmanagement.dto.StockEventDto; import org.openlmis.stockmanagement.exception.ValidationMessageException; import org.openlmis.stockmanagement.repository.StockCardRepository; @@ -63,8 +63,8 @@ public void validate(StockEventDto stockEventDto) { private void checkAllStockCardsCovered(StockEventDto stockEventDto, Profiler profiler) { profiler.start("GET_ORDERABLE_LOT_IDENTITIES"); - List coveredIdentities = stockEventDto.getLineItems().stream() - .map(OrderableLotIdentity::identityOf) + List coveredIdentities = stockEventDto.getLineItems().stream() + .map(OrderableLotUnitIdentity::identityOf) .collect(toList()); profiler.start("FIND_STOCK_CARDS_BY_PROGRAM_AND_FACILITY"); diff --git a/src/main/java/org/openlmis/stockmanagement/validators/OrderableLotDuplicationValidator.java b/src/main/java/org/openlmis/stockmanagement/validators/OrderableLotUnitDuplicationValidator.java similarity index 83% rename from src/main/java/org/openlmis/stockmanagement/validators/OrderableLotDuplicationValidator.java rename to src/main/java/org/openlmis/stockmanagement/validators/OrderableLotUnitDuplicationValidator.java index 5df9fdd5..967868d6 100644 --- a/src/main/java/org/openlmis/stockmanagement/validators/OrderableLotDuplicationValidator.java +++ b/src/main/java/org/openlmis/stockmanagement/validators/OrderableLotUnitDuplicationValidator.java @@ -20,7 +20,7 @@ import java.util.HashSet; import java.util.Set; -import org.openlmis.stockmanagement.domain.identity.OrderableLotIdentity; +import org.openlmis.stockmanagement.domain.identity.OrderableLotUnitIdentity; import org.openlmis.stockmanagement.dto.StockEventDto; import org.openlmis.stockmanagement.exception.ValidationMessageException; import org.openlmis.stockmanagement.util.Message; @@ -33,8 +33,8 @@ * orderable/lot combo. * Such rule is NOT applied to adjustment, issue, receive. */ -@Component(value = "OrderableLotDuplicationValidator") -public class OrderableLotDuplicationValidator implements StockEventValidator { +@Component(value = "OrderableLotUnitDuplicationValidator") +public class OrderableLotUnitDuplicationValidator implements StockEventValidator { @Override public void validate(StockEventDto stockEventDto) { XLOGGER.entry(stockEventDto); @@ -46,12 +46,12 @@ public void validate(StockEventDto stockEventDto) { return; } - Set nonDuplicates = new HashSet<>(); + Set nonDuplicates = new HashSet<>(); profiler.start("GET_DUPLICATES"); - Set duplicates = stockEventDto.getLineItems() - .stream().map(OrderableLotIdentity::identityOf) - .filter(lotIdentity -> !nonDuplicates.add(lotIdentity)) + Set duplicates = stockEventDto.getLineItems() + .stream().map(OrderableLotUnitIdentity::identityOf) + .filter(orderableLotUnitIdentity -> !nonDuplicates.add(orderableLotUnitIdentity)) .collect(toSet()); if (duplicates.size() > 0) { diff --git a/src/main/java/org/openlmis/stockmanagement/validators/QuantityValidator.java b/src/main/java/org/openlmis/stockmanagement/validators/QuantityValidator.java index 041726d0..3d4f98dc 100644 --- a/src/main/java/org/openlmis/stockmanagement/validators/QuantityValidator.java +++ b/src/main/java/org/openlmis/stockmanagement/validators/QuantityValidator.java @@ -21,7 +21,7 @@ import java.util.List; import java.util.Map; -import org.openlmis.stockmanagement.domain.identity.OrderableLotIdentity; +import org.openlmis.stockmanagement.domain.identity.OrderableLotUnitIdentity; import org.openlmis.stockmanagement.dto.StockEventAdjustmentDto; import org.openlmis.stockmanagement.dto.StockEventDto; import org.openlmis.stockmanagement.dto.StockEventLineItemDto; @@ -51,10 +51,10 @@ public void validate(StockEventDto stockEventDto) { } profiler.start("GET_ORDERABLE_GROUPS"); - Map> sameOrderableGroups = stockEventDto + Map> sameOrderableGroups = stockEventDto .getLineItems() .stream() - .collect(groupingBy(OrderableLotIdentity::identityOf)); + .collect(groupingBy(OrderableLotUnitIdentity::identityOf)); for (List group : sameOrderableGroups.values()) { // increase may cause int overflow, decrease may cause below zero diff --git a/src/main/resources/db/migration/20240612140304888__added_stock_card_indexes_with_units.sql b/src/main/resources/db/migration/20240612140304888__added_stock_card_indexes_with_units.sql new file mode 100644 index 00000000..25a2d065 --- /dev/null +++ b/src/main/resources/db/migration/20240612140304888__added_stock_card_indexes_with_units.sql @@ -0,0 +1,21 @@ +-- Drop old indices +DROP INDEX uniq_stock_card_facility_program_product_lot; +DROP INDEX uniq_stock_card_facility_program_product; + +-- https://stackoverflow.com/a/8289253 +-- Create indexes for nullable lot and nullable unit +CREATE UNIQUE INDEX uniq_stock_card_facility_program_product_lot_unit +ON stock_cards(facilityId, programId, orderableId, lotId, unitOfOrderableId) +WHERE lotId IS NOT NULL AND unitOfOrderableId IS NOT NULL; + +CREATE UNIQUE INDEX uniq_stock_card_facility_program_product_lot +ON stock_cards(facilityId, programId, orderableId, lotId) +WHERE lotId IS NOT NULL AND unitOfOrderableId IS NULL; + +CREATE UNIQUE INDEX uniq_stock_card_facility_program_product_unit +ON stock_cards(facilityId, programId, orderableId, unitOfOrderableId) +WHERE lotId IS NULL AND unitOfOrderableId IS NOT NULL; + +CREATE UNIQUE INDEX uniq_stock_card_facility_program_product +ON stock_cards(facilityId, programId, orderableId) +WHERE lotId IS NULL AND unitOfOrderableId IS NULL; diff --git a/src/test/java/org/openlmis/stockmanagement/service/StockCardSummariesServiceTest.java b/src/test/java/org/openlmis/stockmanagement/service/StockCardSummariesServiceTest.java index 91d53c0a..41525665 100644 --- a/src/test/java/org/openlmis/stockmanagement/service/StockCardSummariesServiceTest.java +++ b/src/test/java/org/openlmis/stockmanagement/service/StockCardSummariesServiceTest.java @@ -23,6 +23,7 @@ import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasProperty; +import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.core.Is.is; import static org.junit.Assert.assertEquals; @@ -49,7 +50,7 @@ import org.openlmis.stockmanagement.domain.card.StockCard; import org.openlmis.stockmanagement.domain.event.CalculatedStockOnHand; import org.openlmis.stockmanagement.domain.event.StockEvent; -import org.openlmis.stockmanagement.domain.identity.OrderableLotIdentity; +import org.openlmis.stockmanagement.domain.identity.OrderableLotUnitIdentity; import org.openlmis.stockmanagement.dto.StockCardDto; import org.openlmis.stockmanagement.dto.referencedata.ApprovedProductDto; import org.openlmis.stockmanagement.dto.referencedata.LotDto; @@ -75,55 +76,44 @@ import org.openlmis.stockmanagement.util.RequestParameters; import org.springframework.data.domain.PageImpl; +@SuppressWarnings("PMD.TooManyMethods") @RunWith(MockitoJUnitRunner.class) public class StockCardSummariesServiceTest { + private final UUID facilityId = randomUUID(); + private final UUID programId = randomUUID(); + private final UUID orderableId1 = randomUUID(); + private final UUID orderableId2 = randomUUID(); + private final UUID orderableId3 = randomUUID(); + private final UUID orderableId4 = randomUUID(); + private final UUID orderableId5 = randomUUID(); + private final UUID orderableId6 = randomUUID(); + private final UUID orderableId7 = randomUUID(); @Mock private ApprovedProductReferenceDataService approvedProductReferenceDataService; - @Mock private OrderableFulfillReferenceDataService orderableFulfillReferenceDataService; - @Mock private OrderableReferenceDataService orderableReferenceDataService; - @Mock @SuppressWarnings("PMD") private FacilityReferenceDataService facilityRefDataService; - @Mock @SuppressWarnings("PMD") private ProgramReferenceDataService programRefDataService; - @Mock private LotReferenceDataService lotReferenceDataService; - @Mock private CalculatedStockOnHandService calculatedStockOnHandService; - @Mock private StockCardRepository cardRepository; - @Mock private PermissionService permissionService; - @Mock private CalculatedStockOnHandRepository calculatedStockOnHandRepository; - @InjectMocks private StockCardSummariesService stockCardSummariesService; - private final UUID facilityId = randomUUID(); - private final UUID programId = randomUUID(); - - private final UUID orderableId1 = randomUUID(); - private final UUID orderableId2 = randomUUID(); - private final UUID orderableId3 = randomUUID(); - private final UUID orderableId4 = randomUUID(); - private final UUID orderableId5 = randomUUID(); - private final UUID orderableId6 = randomUUID(); - private final UUID orderableId7 = randomUUID(); - @Test public void shouldCreateDummyCards() { //given @@ -131,6 +121,9 @@ public void shouldCreateDummyCards() { UUID orderable2Id = randomUUID(); UUID orderable3Id = randomUUID(); UUID orderable4Id = randomUUID(); + UUID unitOfOrderable1 = randomUUID(); + UUID unitOfOrderable3 = randomUUID(); + OrderableDto orderable1 = createOrderableDto(orderable1Id, "1"); OrderableDto orderable2 = createOrderableDto(orderable2Id, "2"); @@ -149,8 +142,8 @@ public void shouldCreateDummyCards() { //but only 1, 3 have cards. 2, 4 don't have cards. when(cardRepository.getIdentitiesBy(programId, facilityId)) .thenReturn(asList( - new OrderableLotIdentity(orderable1Id, null), - new OrderableLotIdentity(orderable3Id, null))); + new OrderableLotUnitIdentity(orderable1Id, null, unitOfOrderable1), + new OrderableLotUnitIdentity(orderable3Id, null, unitOfOrderable3))); LotDto lotDto = new LotDto(); lotDto.setId(randomUUID()); @@ -207,9 +200,9 @@ public void shouldFindStockCards() { OrderableDto orderable3 = new OrderableDtoDataBuilder().build(); OrderablesAggregator orderablesAggregator = new OrderablesAggregator(asList( - new ApprovedProductDto(orderable), - new ApprovedProductDto(orderable2), - new ApprovedProductDto(orderable3) + new ApprovedProductDto(orderable), + new ApprovedProductDto(orderable2), + new ApprovedProductDto(orderable3) )); StockCardSummariesV2SearchParams params = new StockCardSummariesV2SearchParamsDataBuilder() @@ -296,7 +289,7 @@ public void shouldAggregateStockCardsByCommodityTypes() { when(orderableFulfillReferenceDataService .findByIds( ImmutableSet.of(orderableId2, orderableId3, - orderableId5, orderableId6, orderableId7))) + orderableId5, orderableId6, orderableId7))) .thenReturn(fulfillMap); StockEvent event = new StockEventDataBuilder() @@ -353,7 +346,8 @@ public void shouldAggregateStockCardsByCommodityTypes() { Map cardMap = stockCardSummariesService.getGroupedStockCards(programId, facilityId, null, - LocalDate.of(2017, 3, 16), LocalDate.of(2017, 3, 19)); + LocalDate.of(2017, 3, 16), + LocalDate.of(2017, 3, 19)); assertThat(cardMap.keySet(), hasItems(orderableId1, orderableId4, orderableId6, orderableId7)); @@ -389,7 +383,7 @@ public void shouldAggregateStockCardsByCommodityTypesWhenNoStartDateProvided() { when(orderableFulfillReferenceDataService .findByIds( ImmutableSet.of(orderableId2, orderableId3, orderableId5, - orderableId6, orderableId7))) + orderableId6, orderableId7))) .thenReturn(fulfillMap); StockEvent event = new StockEventDataBuilder() @@ -435,15 +429,15 @@ public void shouldAggregateStockCardsByCommodityTypesWhenNoStartDateProvided() { .thenReturn(calculatedStockOnHands); final CalculatedStockOnHand calculatedStockOnHand1 = - generateCalculatedStockOnHandWithEndDate(stockCard1); + generateCalculatedStockOnHandWithEndDate(stockCard1); final CalculatedStockOnHand calculatedStockOnHand2 = - generateCalculatedStockOnHandWithEndDate(stockCard2); + generateCalculatedStockOnHandWithEndDate(stockCard2); final CalculatedStockOnHand calculatedStockOnHand3 = - generateCalculatedStockOnHandWithEndDate(stockCard3); + generateCalculatedStockOnHandWithEndDate(stockCard3); final CalculatedStockOnHand calculatedStockOnHand4 = - generateCalculatedStockOnHandWithEndDate(stockCard4); + generateCalculatedStockOnHandWithEndDate(stockCard4); final CalculatedStockOnHand calculatedStockOnHand5 = - generateCalculatedStockOnHandWithEndDate(stockCard5); + generateCalculatedStockOnHandWithEndDate(stockCard5); Map cardMap = stockCardSummariesService.getGroupedStockCards(programId, facilityId, null, @@ -469,6 +463,191 @@ public void shouldAggregateStockCardsByCommodityTypesWhenNoStartDateProvided() { hasItems(calculatedStockOnHand, calculatedStockOnHand5)); } + @Test + public void shouldFindStockCardsForProgramAndFacilityIds() { + //given + UUID programId = UUID.randomUUID(); + UUID facilityId = UUID.randomUUID(); + UUID lotId1 = UUID.randomUUID(); + UUID lotId2 = UUID.randomUUID(); + UUID unitOfOrderable1 = UUID.randomUUID(); + UUID unitOfOrderable2 = UUID.randomUUID(); + StockEvent event = new StockEventDataBuilder() + .withFacility(facilityId) + .withProgram(programId) + .build(); + + StockCard stockCard1 = new StockCardDataBuilder(event) + .withOrderableId(orderableId1) + .withStockOnHand(12) + .withLotId(lotId1) + .withUnitOfOrderableId(unitOfOrderable1) + .build(); + + StockCard stockCard2 = new StockCardDataBuilder(event) + .withOrderableId(orderableId2) + .withStockOnHand(26) + .withLotId(lotId2) + .withUnitOfOrderableId(unitOfOrderable2) + .build(); + + List stockCards = asList(stockCard1, stockCard2); + + when(cardRepository.findByProgramIdAndFacilityId(programId, facilityId)) + .thenReturn(stockCards); + + //when + List stockCardsDtos = + stockCardSummariesService.findStockCards(programId, facilityId); + + //then + assertThat(stockCardsDtos, hasSize(2)); + checkStockCardDto(stockCardsDtos, orderableId1, lotId1, unitOfOrderable1); + checkStockCardDto(stockCardsDtos, orderableId2, lotId2, unitOfOrderable2); + } + + @Test + public void shouldOrderableLotIdentityBeEqualToOrderableLotUnitIdentity() { + //given + UUID orderableId = UUID.randomUUID(); + UUID lotId = UUID.randomUUID(); + UUID unitOfOrderableId = UUID.randomUUID(); + StockCardSummariesService.OrderableLotIdentity orderableLotIdentity = + new StockCardSummariesService.OrderableLotIdentity( + orderableId, + lotId + ); + OrderableLotUnitIdentity orderableLotUnitIdentity = + new OrderableLotUnitIdentity( + orderableId, + lotId, + unitOfOrderableId + ); + + //when + boolean areEqual = + orderableLotIdentity.equalsOrderableLotUnitIdentity(orderableLotUnitIdentity); + + //then + assertThat(areEqual, is(true)); + } + + @Test + public void shouldOrderableLotIdentityIsNotEqualToOrderableLotUnitIdentityDueToOrderableIds() { + //given + UUID orderableId1 = UUID.randomUUID(); + UUID orderableId2 = UUID.randomUUID(); + + UUID lotId = UUID.randomUUID(); + UUID unitOfOrderableId = UUID.randomUUID(); + StockCardSummariesService.OrderableLotIdentity orderableLotIdentity = + new StockCardSummariesService.OrderableLotIdentity( + orderableId1, + lotId + ); + OrderableLotUnitIdentity orderableLotUnitIdentity = + new OrderableLotUnitIdentity( + orderableId2, + lotId, + unitOfOrderableId + ); + + //when + boolean areEqual = + orderableLotIdentity.equalsOrderableLotUnitIdentity(orderableLotUnitIdentity); + + //then + assertThat(areEqual, is(false)); + } + + @Test + public void shouldOrderableLotIdentityIsNotEqualToOrderableLotUnitIdentityDueToLotIds() { + //given + UUID orderableId = UUID.randomUUID(); + UUID lotId1 = UUID.randomUUID(); + UUID lotId2 = UUID.randomUUID(); + UUID unitOfOrderableId = UUID.randomUUID(); + StockCardSummariesService.OrderableLotIdentity orderableLotIdentity = + new StockCardSummariesService.OrderableLotIdentity( + orderableId, + lotId1 + ); + OrderableLotUnitIdentity orderableLotUnitIdentity = + new OrderableLotUnitIdentity( + orderableId, + lotId2, + unitOfOrderableId + ); + + //when + boolean areEqual = + orderableLotIdentity.equalsOrderableLotUnitIdentity(orderableLotUnitIdentity); + + //then + assertThat(areEqual, is(false)); + } + + @Test + public void shouldOrderableLotIdentityIsNotEqualToNull() { + //given + UUID orderableId = UUID.randomUUID(); + UUID lotId = UUID.randomUUID(); + StockCardSummariesService.OrderableLotIdentity orderableLotIdentity = + new StockCardSummariesService.OrderableLotIdentity( + orderableId, + lotId + ); + OrderableLotUnitIdentity orderableLotUnitIdentity = null; + + //when + boolean areEqual = + orderableLotIdentity.equalsOrderableLotUnitIdentity(orderableLotUnitIdentity); + + //then + assertThat(areEqual, is(false)); + } + + @Test + public void shouldOrderableLotIdentityIsNotEqualDueToNullOrderableId() { + //given + UUID orderableId2 = UUID.randomUUID(); + UUID lotId1 = UUID.randomUUID(); + UUID lotId2 = UUID.randomUUID(); + UUID unitOfOrderableId = UUID.randomUUID(); + StockCardSummariesService.OrderableLotIdentity orderableLotIdentity = + new StockCardSummariesService.OrderableLotIdentity( + null, + lotId1 + ); + OrderableLotUnitIdentity orderableLotUnitIdentity = + new OrderableLotUnitIdentity( + orderableId2, + lotId2, + unitOfOrderableId + ); + + //when + boolean areEqual = + orderableLotIdentity.equalsOrderableLotUnitIdentity(orderableLotUnitIdentity); + + //then + assertThat(areEqual, is(false)); + } + + private void checkStockCardDto( + List stockCardsDtos, + UUID orderableId1, + UUID lotId1, + UUID unitOfOrderable1) { + Optional stockCardDtoOptional = stockCardsDtos.stream() + .filter(stockCardDto -> stockCardDto.getOrderableId().equals(orderableId1)) + .findFirst(); + assertThat(stockCardDtoOptional.isPresent(), is(true)); + StockCardDto stockCardDto = stockCardDtoOptional.get(); + assertThat(stockCardDto.getLotId(), is(lotId1)); + assertThat(stockCardDto.getUnitOfOrderableId(), is(unitOfOrderable1)); + } + private OrderableDto createOrderableDto(UUID orderableId, String productName) { return OrderableDto.builder() .id(orderableId) @@ -489,9 +668,9 @@ private CalculatedStockOnHand generateAndMockCalculatedStockOnHand(StockCard sto private CalculatedStockOnHand generateCalculatedStockOnHandWithEndDate(StockCard stockCard) { CalculatedStockOnHand calculatedStockOnHand = new CalculatedStockOnHandDataBuilder().build(); when(calculatedStockOnHandRepository - .findFirstByStockCardIdAndOccurredDateLessThanEqualOrderByOccurredDateDesc( - stockCard.getId(), LocalDate.of(2017, 3, 19))) - .thenReturn(Optional.ofNullable(calculatedStockOnHand)); + .findFirstByStockCardIdAndOccurredDateLessThanEqualOrderByOccurredDateDesc( + stockCard.getId(), LocalDate.of(2017, 3, 19))) + .thenReturn(Optional.ofNullable(calculatedStockOnHand)); return calculatedStockOnHand; } diff --git a/src/test/java/org/openlmis/stockmanagement/service/StockEventNotificationProcessorTest.java b/src/test/java/org/openlmis/stockmanagement/service/StockEventNotificationProcessorTest.java index f1c117d0..e30b9649 100644 --- a/src/test/java/org/openlmis/stockmanagement/service/StockEventNotificationProcessorTest.java +++ b/src/test/java/org/openlmis/stockmanagement/service/StockEventNotificationProcessorTest.java @@ -38,7 +38,7 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.openlmis.stockmanagement.domain.card.StockCard; -import org.openlmis.stockmanagement.domain.identity.OrderableLotIdentity; +import org.openlmis.stockmanagement.domain.identity.OrderableLotUnitIdentity; import org.openlmis.stockmanagement.dto.StockEventDto; import org.openlmis.stockmanagement.dto.StockEventLineItemDto; import org.openlmis.stockmanagement.dto.referencedata.RightDto; @@ -90,6 +90,7 @@ public void setUp() { firstLineItem = stockEventDto.getLineItems().get(0); firstLineItem.setOrderableId(orderableId); firstLineItem.setLotId(lotId); + firstLineItem.setUnitOfOrderableId(unitOfOrderableId); firstLineItem.setQuantity(0); stockEventDto.setContext(context); @@ -101,7 +102,7 @@ public void setUp() { @Test public void shouldCallStockoutNotifierWhenStockOnHandIsZero() throws Exception { //given - when(context.findCard(any(OrderableLotIdentity.class))).thenReturn(stockCard); + when(context.findCard(any(OrderableLotUnitIdentity.class))).thenReturn(stockCard); //when stockEventNotificationProcessor.callAllNotifications(stockEventDto); @@ -120,28 +121,36 @@ public void shouldCallStockoutNotifierWhenStockOnHandIsZero() throws Exception { @Test public void shouldCallStockoutNotifierForEveryCard() throws Exception { //given - UUID anotherStockCardId = UUID.randomUUID(); - UUID anotherOrderableId = UUID.randomUUID(); - UUID anotherLotId = UUID.randomUUID(); - - StockCard anotherStockCard = new StockCard(null, facilityId, programId, orderableId, lotId, - unitOfOrderableId, null, 0, getBaseDate(), getBaseDateTime(), true); + final UUID anotherStockCardId = UUID.randomUUID(); + final UUID anotherOrderableId = UUID.randomUUID(); + final UUID anotherLotId = UUID.randomUUID(); + final UUID anotherUnitOfOrderableId = UUID.randomUUID(); + + StockCard anotherStockCard = new StockCard(null, facilityId, programId, orderableId, + lotId, unitOfOrderableId, + null, 0, getBaseDate(), getBaseDateTime(), true); anotherStockCard.setId(anotherStockCardId); StockEventLineItemDto secondLineItem = createStockEventLineItem(); secondLineItem.setOrderableId(anotherOrderableId); secondLineItem.setLotId(anotherLotId); + secondLineItem.setUnitOfOrderableId(anotherUnitOfOrderableId); secondLineItem.setQuantity(0); stockEventDto.setLineItems(Arrays.asList(firstLineItem, secondLineItem)); - when(context.findCard(new OrderableLotIdentity(orderableId, lotId))).thenReturn(stockCard); - when(context.findCard(new OrderableLotIdentity(anotherOrderableId, anotherLotId))) + when(context.findCard(new OrderableLotUnitIdentity(orderableId, lotId, unitOfOrderableId))) + .thenReturn(stockCard); + when(context.findCard( + new OrderableLotUnitIdentity(anotherOrderableId, anotherLotId, anotherUnitOfOrderableId) + )) .thenReturn(anotherStockCard); //when stockEventNotificationProcessor.callAllNotifications(stockEventDto); //then - verify(stockoutNotifier, times(2)).notifyStockEditors(any(StockCard.class), eq((rightId))); + verify(stockoutNotifier, times(2)).notifyStockEditors( + any(StockCard.class), eq((rightId)) + ); } } diff --git a/src/integration-test/java/org/openlmis/stockmanagement/service/StockEventValidationsServiceIntegrationTest.java b/src/test/java/org/openlmis/stockmanagement/service/StockEventValidationsServiceTest.java similarity index 88% rename from src/integration-test/java/org/openlmis/stockmanagement/service/StockEventValidationsServiceIntegrationTest.java rename to src/test/java/org/openlmis/stockmanagement/service/StockEventValidationsServiceTest.java index bca2168d..b70330db 100644 --- a/src/integration-test/java/org/openlmis/stockmanagement/service/StockEventValidationsServiceIntegrationTest.java +++ b/src/test/java/org/openlmis/stockmanagement/service/StockEventValidationsServiceTest.java @@ -26,7 +26,9 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.openlmis.stockmanagement.BaseIntegrationTest; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; import org.openlmis.stockmanagement.dto.StockEventDto; import org.openlmis.stockmanagement.exception.ValidationMessageException; import org.openlmis.stockmanagement.extension.ExtensionManager; @@ -42,7 +44,7 @@ import org.openlmis.stockmanagement.validators.DefaultUnpackKitValidator; import org.openlmis.stockmanagement.validators.LotValidator; import org.openlmis.stockmanagement.validators.MandatoryFieldsValidator; -import org.openlmis.stockmanagement.validators.OrderableLotDuplicationValidator; +import org.openlmis.stockmanagement.validators.OrderableLotUnitDuplicationValidator; import org.openlmis.stockmanagement.validators.PhysicalInventoryAdjustmentReasonsValidator; import org.openlmis.stockmanagement.validators.QuantityValidator; import org.openlmis.stockmanagement.validators.ReasonExistenceValidator; @@ -51,64 +53,59 @@ import org.openlmis.stockmanagement.validators.SourceDestinationGeoLevelAffinityValidator; import org.openlmis.stockmanagement.validators.StockEventVvmValidator; import org.openlmis.stockmanagement.validators.UnitOfOrderableValidator; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.junit4.SpringRunner; -@RunWith(SpringRunner.class) -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -public class StockEventValidationsServiceIntegrationTest extends BaseIntegrationTest { +@RunWith(MockitoJUnitRunner.class) +public class StockEventValidationsServiceTest { - @Autowired + @InjectMocks private StockEventValidationsService stockEventValidationsService; - @MockBean + @Mock private StockEventVvmValidator stockEventVvmValidator; - @MockBean + @Mock private ApprovedOrderableValidator approvedOrderableValidator; - @MockBean + @Mock private SourceDestinationAssignmentValidator sourceDestinationAssignmentValidator; - @MockBean + @Mock private MandatoryFieldsValidator mandatoryFieldsValidator; - @MockBean + @Mock private ReceiveIssueReasonValidator receiveIssueReasonValidator; - @MockBean + @Mock private DefaultAdjustmentReasonValidator adjustmentReasonValidator; - @MockBean + @Mock private DefaultFreeTextValidator freeTextValidator; - @MockBean + @Mock private QuantityValidator quantityValidator; - @MockBean + @Mock private LotValidator lotValidator; - @MockBean + @Mock private ReasonExistenceValidator reasonExistenceValidator; - @MockBean - private OrderableLotDuplicationValidator orderableLotDuplicationValidator; + @Mock + private OrderableLotUnitDuplicationValidator orderableLotUnitDuplicationValidator; - @MockBean + @Mock private PhysicalInventoryAdjustmentReasonsValidator physicalInventoryReasonsValidator; - @MockBean + @Mock private DefaultUnpackKitValidator unpackKitValidator; - @MockBean + @Mock private SourceDestinationGeoLevelAffinityValidator sourceDestinationGeoLeveLAffinityValidator; - @MockBean + @Mock private ExtensionManager extensionManager; - @MockBean + @Mock private UnitOfOrderableValidator unitOfOrderableValidator; @Before @@ -125,7 +122,7 @@ public void setUp() throws Exception { doNothing().when(adjustmentReasonValidator).validate(any(StockEventDto.class)); doNothing().when(quantityValidator).validate(any(StockEventDto.class)); doNothing().when(lotValidator).validate(any(StockEventDto.class)); - doNothing().when(orderableLotDuplicationValidator).validate(any(StockEventDto.class)); + doNothing().when(orderableLotUnitDuplicationValidator).validate(any(StockEventDto.class)); doNothing().when(reasonExistenceValidator).validate(any(StockEventDto.class)); doNothing().when(physicalInventoryReasonsValidator).validate(any(StockEventDto.class)); doNothing().when(unpackKitValidator).validate(any(StockEventDto.class)); @@ -158,7 +155,7 @@ public void shouldValidateWithAllImplementationsOfValidators() throws Exception verify(receiveIssueReasonValidator, times(1)).validate(stockEventDto); verify(quantityValidator, times(1)).validate(stockEventDto); verify(lotValidator, times(1)).validate(stockEventDto); - verify(orderableLotDuplicationValidator, times(1)).validate(stockEventDto); + verify(orderableLotUnitDuplicationValidator, times(1)).validate(stockEventDto); verify(reasonExistenceValidator, times(1)).validate(stockEventDto); verify(physicalInventoryReasonsValidator, times(1)).validate(stockEventDto); verify(adjustmentReasonValidator, times(1)).validate(stockEventDto); diff --git a/src/test/java/org/openlmis/stockmanagement/util/OrderableLotIdentityTest.java b/src/test/java/org/openlmis/stockmanagement/util/OrderableLotIdentityTest.java deleted file mode 100644 index 8483977b..00000000 --- a/src/test/java/org/openlmis/stockmanagement/util/OrderableLotIdentityTest.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This program is part of the OpenLMIS logistics management information system platform software. - * Copyright © 2017 VillageReach - * - * This program is free software: you can redistribute it and/or modify it under the terms - * of the GNU Affero General Public License as published by the Free Software Foundation, either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Affero General Public License for more details. You should have received a copy of - * the GNU Affero General Public License along with this program. If not, see - * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. - */ - -package org.openlmis.stockmanagement.util; - -import static java.util.UUID.fromString; -import static java.util.UUID.randomUUID; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; -import org.openlmis.stockmanagement.domain.identity.OrderableLotIdentity; - -public class OrderableLotIdentityTest { - @Test - public void sameOrderableAndLotIdShouldEqualAndHaveSameHash() throws Exception { - //given - OrderableLotIdentity identity1 = new OrderableLotIdentity(randomUUID(), randomUUID()); - - OrderableLotIdentity identity2 = new OrderableLotIdentity( - fromString(identity1.getOrderableId().toString()), - fromString(identity1.getLotId().toString())); - - //when - boolean equals = identity1.equals(identity2); - int identity1Hash = identity1.hashCode(); - int identity2Hash = identity2.hashCode(); - - //then - assertTrue(equals); - assertEquals(identity1Hash, identity2Hash); - } -} \ No newline at end of file diff --git a/src/test/java/org/openlmis/stockmanagement/util/OrderableLotUnitIdentityTest.java b/src/test/java/org/openlmis/stockmanagement/util/OrderableLotUnitIdentityTest.java new file mode 100644 index 00000000..dfb825bd --- /dev/null +++ b/src/test/java/org/openlmis/stockmanagement/util/OrderableLotUnitIdentityTest.java @@ -0,0 +1,162 @@ +/* + * This program is part of the OpenLMIS logistics management information system platform software. + * Copyright © 2017 VillageReach + * + * This program is free software: you can redistribute it and/or modify it under the terms + * of the GNU Affero General Public License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Affero General Public License for more details. You should have received a copy of + * the GNU Affero General Public License along with this program. If not, see + * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org. + */ + +package org.openlmis.stockmanagement.util; + +import static java.util.UUID.fromString; +import static java.util.UUID.randomUUID; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.UUID; +import org.junit.Test; +import org.openlmis.stockmanagement.domain.identity.OrderableLotUnitIdentity; + +public class OrderableLotUnitIdentityTest { + @Test + public void sameOrderableAndLotIdShouldEqualAndHaveSameHash() throws Exception { + //given + OrderableLotUnitIdentity identity1 = new OrderableLotUnitIdentity(randomUUID(), randomUUID(), + randomUUID()); + + OrderableLotUnitIdentity identity2 = new OrderableLotUnitIdentity( + fromString(identity1.getOrderableId().toString()), + fromString(identity1.getLotId().toString()), + fromString(identity1.getUnitOfOrderableId().toString())); + + //when + boolean equals = identity1.equals(identity2); + int identity1Hash = identity1.hashCode(); + int identity2Hash = identity2.hashCode(); + + //then + assertTrue(equals); + assertEquals(identity1Hash, identity2Hash); + } + + @Test + public void shouldNotBeEqualDueToOrderableId() { + //given + UUID orderableId1 = UUID.randomUUID(); + UUID orderableId2 = UUID.randomUUID(); + + UUID lotId = UUID.randomUUID(); + UUID unitOfOrderableId = UUID.randomUUID(); + + OrderableLotUnitIdentity orderableLotUnitIdentity1 = + new OrderableLotUnitIdentity( + orderableId1, + lotId, + unitOfOrderableId + ); + OrderableLotUnitIdentity orderableLotUnitIdentity2 = + new OrderableLotUnitIdentity( + orderableId2, + lotId, + unitOfOrderableId + ); + + //when + boolean areEqual = + orderableLotUnitIdentity1.equals(orderableLotUnitIdentity2); + + //then + assertFalse(areEqual); + } + + @Test + public void shouldNotBeEqualDueToLotId() { + //given + UUID orderableId = UUID.randomUUID(); + + UUID lotId1 = UUID.randomUUID(); + UUID lotId2 = UUID.randomUUID(); + + UUID unitOfOrderableId = UUID.randomUUID(); + + OrderableLotUnitIdentity orderableLotUnitIdentity1 = + new OrderableLotUnitIdentity( + orderableId, + lotId1, + unitOfOrderableId + ); + OrderableLotUnitIdentity orderableLotUnitIdentity2 = + new OrderableLotUnitIdentity( + orderableId, + lotId2, + unitOfOrderableId + ); + + //when + boolean areEqual = + orderableLotUnitIdentity1.equals(orderableLotUnitIdentity2); + + //then + assertFalse(areEqual); + } + + @Test + public void shouldNotBeEqualDueToUnitOfOrderableId() { + //given + UUID orderableId = UUID.randomUUID(); + UUID lotId = UUID.randomUUID(); + UUID unitOfOrderableId1 = UUID.randomUUID(); + UUID unitOfOrderableId2 = UUID.randomUUID(); + + OrderableLotUnitIdentity orderableLotUnitIdentity1 = + new OrderableLotUnitIdentity( + orderableId, + lotId, + unitOfOrderableId1 + ); + OrderableLotUnitIdentity orderableLotUnitIdentity2 = + new OrderableLotUnitIdentity( + orderableId, + lotId, + unitOfOrderableId2 + ); + + //when + boolean areEqual = + orderableLotUnitIdentity1.equals(orderableLotUnitIdentity2); + + //then + assertFalse(areEqual); + } + + @Test + public void shouldNotBeEqualDueToSecondIdentityIsNull() { + //given + UUID orderableId = UUID.randomUUID(); + UUID lotId = UUID.randomUUID(); + UUID unitOfOrderableId = UUID.randomUUID(); + + OrderableLotUnitIdentity orderableLotUnitIdentity1 = + new OrderableLotUnitIdentity( + orderableId, + lotId, + unitOfOrderableId + ); + OrderableLotUnitIdentity orderableLotUnitIdentity2 = null; + + //when + boolean areEqual = + orderableLotUnitIdentity1.equals(orderableLotUnitIdentity2); + + //then + assertFalse(areEqual); + } +} diff --git a/src/test/java/org/openlmis/stockmanagement/validators/ActiveStockCardsValidatorTest.java b/src/test/java/org/openlmis/stockmanagement/validators/ActiveStockCardsValidatorTest.java index ba1e7d28..550baee2 100644 --- a/src/test/java/org/openlmis/stockmanagement/validators/ActiveStockCardsValidatorTest.java +++ b/src/test/java/org/openlmis/stockmanagement/validators/ActiveStockCardsValidatorTest.java @@ -28,7 +28,7 @@ import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; -import org.openlmis.stockmanagement.domain.identity.OrderableLotIdentity; +import org.openlmis.stockmanagement.domain.identity.OrderableLotUnitIdentity; import org.openlmis.stockmanagement.dto.StockEventDto; import org.openlmis.stockmanagement.repository.StockCardRepository; @@ -54,7 +54,8 @@ public void shouldThrowExceptionIfExistingCardOrderableNotCovered() throws Excep when(stockCardRepository .getIdentitiesBy(stockEventDto.getProgramId(), stockEventDto.getFacilityId())) - .thenReturn(singletonList(new OrderableLotIdentity(randomUUID(), randomUUID()))); + .thenReturn(singletonList(new OrderableLotUnitIdentity(randomUUID(), randomUUID(), + randomUUID()))); //when activeStockCardsValidator.validate(stockEventDto); @@ -71,7 +72,8 @@ public void shouldThrowExceptionIfExistingCardLotNotCovered() throws Exception { when(stockCardRepository .getIdentitiesBy(stockEventDto.getProgramId(), stockEventDto.getFacilityId())) - .thenReturn(singletonList(new OrderableLotIdentity(randomUUID(), randomUUID()))); + .thenReturn(singletonList(new OrderableLotUnitIdentity(randomUUID(), randomUUID(), + randomUUID()))); //when activeStockCardsValidator.validate(stockEventDto); diff --git a/src/test/java/org/openlmis/stockmanagement/validators/OrderableLotDuplicationValidatorTest.java b/src/test/java/org/openlmis/stockmanagement/validators/OrderableLotUnitDuplicationValidatorTest.java similarity index 75% rename from src/test/java/org/openlmis/stockmanagement/validators/OrderableLotDuplicationValidatorTest.java rename to src/test/java/org/openlmis/stockmanagement/validators/OrderableLotUnitDuplicationValidatorTest.java index db8b265a..deb27dbb 100644 --- a/src/test/java/org/openlmis/stockmanagement/validators/OrderableLotDuplicationValidatorTest.java +++ b/src/test/java/org/openlmis/stockmanagement/validators/OrderableLotUnitDuplicationValidatorTest.java @@ -20,6 +20,7 @@ import static org.junit.rules.ExpectedException.none; import static org.openlmis.stockmanagement.i18n.MessageKeys.ERROR_EVENT_ORDERABLE_LOT_DUPLICATION; +import java.time.LocalDate; import java.util.Arrays; import java.util.UUID; import org.junit.Rule; @@ -30,15 +31,17 @@ import org.mockito.runners.MockitoJUnitRunner; import org.openlmis.stockmanagement.dto.StockEventDto; import org.openlmis.stockmanagement.dto.StockEventLineItemDto; +import org.openlmis.stockmanagement.testutils.StockEventDtoDataBuilder; +import org.openlmis.stockmanagement.testutils.StockEventLineItemDtoDataBuilder; @RunWith(MockitoJUnitRunner.class) -public class OrderableLotDuplicationValidatorTest { +public class OrderableLotUnitDuplicationValidatorTest { @Rule public ExpectedException expectedEx = none(); @InjectMocks - private OrderableLotDuplicationValidator orderableLotDuplicationValidator; + private OrderableLotUnitDuplicationValidator orderableLotUnitDuplicationValidator; @Test public void adjustmentEventWithSameOrderableAndLotAppearTwiceShouldPass() @@ -46,7 +49,7 @@ public void adjustmentEventWithSameOrderableAndLotAppearTwiceShouldPass() StockEventDto eventDto = createStockEventDtoWithDuplicateOrderableLot(randomUUID()); //when - orderableLotDuplicationValidator.validate(eventDto); + orderableLotUnitDuplicationValidator.validate(eventDto); } @Test @@ -57,7 +60,29 @@ public void physicalInventoryEventWithSameOrderableAndLotAppearTwiceShouldNotPas StockEventDto eventDto = createStockEventDtoWithDuplicateOrderableLot(null); //when - orderableLotDuplicationValidator.validate(eventDto); + orderableLotUnitDuplicationValidator.validate(eventDto); + } + + @Test + public void shouldValidateCorrectlyWhenNoDuplicates() { + //given + StockEventDto eventDto = createEventDto(); + + //when + orderableLotUnitDuplicationValidator.validate(eventDto); + + //then + //no exception - ok + } + + private StockEventDto createEventDto() { + return new StockEventDtoDataBuilder() + .addLineItem(new StockEventLineItemDtoDataBuilder() + .withReasonId(UUID.randomUUID()) + .withQuantity(10) + .withOccurredDate(LocalDate.now()) + .buildForAdjustment()) + .build(); } private StockEventDto createStockEventDtoWithDuplicateOrderableLot(UUID reasonId) {