Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OLMIS-7911-duplicate validator #43

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.openlmis.stockmanagement.validators.DefaultAdjustmentReasonValidator;
import org.openlmis.stockmanagement.validators.DefaultFreeTextValidator;
import org.openlmis.stockmanagement.validators.DefaultUnpackKitValidator;
import org.openlmis.stockmanagement.validators.DuplicateTransactionValidator;
import org.openlmis.stockmanagement.validators.LotValidator;
import org.openlmis.stockmanagement.validators.MandatoryFieldsValidator;
import org.openlmis.stockmanagement.validators.OrderableLotDuplicationValidator;
Expand Down Expand Up @@ -104,6 +105,9 @@ public class StockEventValidationsServiceIntegrationTest extends BaseIntegration
@MockBean
private SourceDestinationGeoLevelAffinityValidator sourceDestinationGeoLeveLAffinityValidator;

@MockBean
private DuplicateTransactionValidator duplicateTransactionValidator;

@MockBean
private ExtensionManager extensionManager;

Expand All @@ -125,6 +129,7 @@ public void setUp() throws Exception {
doNothing().when(reasonExistenceValidator).validate(any(StockEventDto.class));
doNothing().when(physicalInventoryReasonsValidator).validate(any(StockEventDto.class));
doNothing().when(unpackKitValidator).validate(any(StockEventDto.class));
doNothing().when(duplicateTransactionValidator).validate(any(StockEventDto.class));
when(extensionManager
.getExtension(ExtensionPointId.ADJUSTMENT_REASON_POINT_ID, AdjustmentReasonValidator.class))
.thenReturn(adjustmentReasonValidator);
Expand Down Expand Up @@ -159,6 +164,7 @@ public void shouldValidateWithAllImplementationsOfValidators() throws Exception
verify(adjustmentReasonValidator, times(1)).validate(stockEventDto);
verify(freeTextValidator, times(1)).validate(stockEventDto);
verify(unpackKitValidator, times(1)).validate(stockEventDto);
verify(duplicateTransactionValidator, times(1)).validate(stockEventDto);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@ public abstract class MessageKeys {
+ ".orderable.and.lot.duplication";
//stock events creation: lot
public static final String ERROR_EVENT_LOT_NOT_EXIST = EVENT_ERROR_PREFIX + ".lot.not.exist";
public static final String ERROR_EVENT_IS_DUPLICATE = EVENT_ERROR_PREFIX + ".is.duplicate";
public static final String ERROR_EVENT_LOT_ORDERABLE_NOT_MATCH = EVENT_ERROR_PREFIX
+ ".lot.not.match.orderable";
public static final String ERROR_EVENT_CANNOT_UNPACK_REGULAR_ORDERABLE = EVENT_ERROR_PREFIX
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,39 @@
package org.openlmis.stockmanagement.repository;

import java.util.UUID;
import org.hibernate.jpa.TypedParameterValue;
import org.openlmis.stockmanagement.domain.card.StockCardLineItem;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface StockCardLineItemRepository
extends PagingAndSortingRepository<StockCardLineItem, UUID> {
extends PagingAndSortingRepository<StockCardLineItem, UUID> {
@Query(value = "SELECT EXISTS (SELECT 1 "
+ "FROM stockmanagement.stock_card_line_items scli "
+ "JOIN stockmanagement.stock_cards sc ON sc.id = scli.stockcardid "
+ "WHERE sc.facilityid= :facilityId "
+ "AND (cast(:lotId as uuid) IS NULL OR sc.lotId= :lotId) "
+ "AND sc.orderableid= :orderableId "
+ "AND (cast(:destinationId as uuid) IS NULL OR scli.destinationid= :destinationId) "
+ "AND (cast(:sourceId as uuid) IS NULL OR scli.sourceId= :sourceId) "
+ "AND scli.occurreddate= CAST(:occurredDate AS DATE) "
+ "AND scli.quantity = :quantity "
+ "AND (cast(:reasonId as uuid) IS NULL OR scli.reasonid= :reasonId) "
+ "AND (cast(:vvmStatus as VARCHAR) "
+ "IS NULL OR scli.extradata ->> 'vvmStatus' = :vvmStatus))",
nativeQuery = true)
boolean getByAllGivenFields(
UUID facilityId,
TypedParameterValue lotId,
UUID orderableId,
TypedParameterValue destinationId,
TypedParameterValue sourceId,
String occurredDate,
int quantity,
TypedParameterValue reasonId,
TypedParameterValue vvmStatus
);

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.openlmis.stockmanagement.extension.point.FreeTextValidator;
import org.openlmis.stockmanagement.extension.point.UnpackKitValidator;
import org.openlmis.stockmanagement.validators.ApprovedOrderableValidator;
import org.openlmis.stockmanagement.validators.DuplicateTransactionValidator;
import org.openlmis.stockmanagement.validators.LotValidator;
import org.openlmis.stockmanagement.validators.MandatoryFieldsValidator;
import org.openlmis.stockmanagement.validators.OrderableLotDuplicationValidator;
Expand All @@ -43,6 +44,8 @@
@Service
public class StockEventValidationsService {

@Autowired
private DuplicateTransactionValidator duplicateTransactionValidator;
@Autowired
private ApprovedOrderableValidator approvedOrderableValidator;

Expand Down Expand Up @@ -85,6 +88,7 @@ public class StockEventValidationsService {
* @param stockEventDto the event to be validated.
*/
public void validate(StockEventDto stockEventDto) {
duplicateTransactionValidator.validate(stockEventDto);
approvedOrderableValidator.validate(stockEventDto);
lotValidator.validate(stockEventDto);
mandatoryFieldsValidator.validate(stockEventDto);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/*
* 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 [email protected].
*/

package org.openlmis.stockmanagement.validators;

import static org.openlmis.stockmanagement.i18n.MessageKeys.ERROR_EVENT_IS_DUPLICATE;

import java.time.format.DateTimeFormatter;
import java.util.UUID;
import org.hibernate.jpa.TypedParameterValue;
import org.hibernate.type.PostgresUUIDType;
import org.hibernate.type.StringType;
import org.openlmis.stockmanagement.dto.StockEventDto;
import org.openlmis.stockmanagement.dto.StockEventLineItemDto;
import org.openlmis.stockmanagement.exception.ValidationMessageException;
import org.openlmis.stockmanagement.repository.StockCardLineItemRepository;
import org.openlmis.stockmanagement.util.Message;
import org.openlmis.stockmanagement.validators.StockEventValidator;
import org.slf4j.profiler.Profiler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


/**
* This validator ensures that the stock event being sent in is not a duplicate, by
* making sure that all line items are not duplicated from a previous event in the same day.
*/
@Component(value = "DuplicateValidator")
public class DuplicateTransactionValidator implements StockEventValidator {
private DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
@Autowired
private StockCardLineItemRepository stockCardLineItemRepository;

@Override
public void validate(StockEventDto stockEventDto) {
XLOGGER.entry(stockEventDto);
Profiler profiler = new Profiler("DUPLICATE_VALIDATOR");
profiler.setLogger(XLOGGER);

if (!stockEventDto.hasLineItems()) {
return;
}
validateNotDuplicate(stockEventDto);

profiler.stop().log();
XLOGGER.exit(stockEventDto);
}

private void validateNotDuplicate(StockEventDto stockEventDto) {
int lineItemCount = stockEventDto.getLineItems().size();
int duplicateCount = 0;
for (StockEventLineItemDto lineItem : stockEventDto.getLineItems()) {
boolean isDuplicate = checkDuplicate(lineItem, stockEventDto.getFacilityId());
if (isDuplicate) {
duplicateCount += 1;
}
}
System.out.println("number of items: " + lineItemCount);
System.out.println("number of duplicates: " + duplicateCount);
if (duplicateCount == lineItemCount) {
throw new ValidationMessageException(
new Message(ERROR_EVENT_IS_DUPLICATE));
}
}

private boolean checkDuplicate(
StockEventLineItemDto stockEventLineItemDto, UUID facilityId) {
TypedParameterValue lotId = new TypedParameterValue(
PostgresUUIDType.INSTANCE, stockEventLineItemDto.getLotId());
TypedParameterValue destinationId = new TypedParameterValue(
PostgresUUIDType.INSTANCE, stockEventLineItemDto.getDestinationId());
TypedParameterValue sourceId = new TypedParameterValue(
PostgresUUIDType.INSTANCE, stockEventLineItemDto.getSourceId());
TypedParameterValue reasonId = new TypedParameterValue(
PostgresUUIDType.INSTANCE, stockEventLineItemDto.getReasonId());
TypedParameterValue vvmStatus = new TypedParameterValue(
StringType.INSTANCE, stockEventLineItemDto.getExtraData().get("vvmStatus"));

boolean duplicatesExist =
stockCardLineItemRepository.getByAllGivenFields(
facilityId, lotId,
stockEventLineItemDto.getOrderableId(),
destinationId, sourceId,
stockEventLineItemDto.getOccurredDate().format(formatter),
stockEventLineItemDto.getQuantity(),
reasonId,
vvmStatus);

return duplicatesExist;
}

}
1 change: 1 addition & 0 deletions src/main/resources/messages_en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ stockmanagement.error.event.orderable.not.in.approvedList=The following orderabl
stockmanagement.error.event.orderable.disabled.vvm=VVM Status configuration is not enabled for orderable: {0}.
#stock event creation: lot
stockmanagement.error.event.lot.not.exist=Lot {0} does not exist.
stockmanagement.error.event.is.duplicate=Event is duplicate, please check stock before proceeding.
stockmanagement.error.event.lot.not.match.orderable=Lot {0} is not under {1}.
#stock event creation: soh
#stock card line item reason
Expand Down
Loading