/*
* This program is part of the OpenLMIS logistics management information
* system platform software.
*
* Copyright © 2015 ThoughtWorks, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. This program is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details. You should
* have received a copy of the GNU Affero General Public License along with
* this program. If not, see http://www.gnu.org/licenses. For additional
* information contact info@OpenLMIS.org
*/
package org.openlmis.core.model.repository;
import android.support.annotation.NonNull;
import org.joda.time.DateTime;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.openlmis.core.LMISRepositoryUnitTest;
import org.openlmis.core.LMISTestApp;
import org.openlmis.core.LMISTestRunner;
import org.openlmis.core.exceptions.LMISException;
import org.openlmis.core.manager.MovementReasonManager;
import org.openlmis.core.model.Lot;
import org.openlmis.core.model.LotMovementItem;
import org.openlmis.core.model.LotOnHand;
import org.openlmis.core.model.Period;
import org.openlmis.core.model.Product;
import org.openlmis.core.model.ProductProgram;
import org.openlmis.core.model.Program;
import org.openlmis.core.model.RnRForm;
import org.openlmis.core.model.StockCard;
import org.openlmis.core.model.StockMovementItem;
import org.openlmis.core.model.builder.LotMovementItemBuilder;
import org.openlmis.core.model.builder.ProductBuilder;
import org.openlmis.core.model.builder.ProductProgramBuilder;
import org.openlmis.core.model.builder.ProgramBuilder;
import org.openlmis.core.model.builder.StockCardBuilder;
import org.openlmis.core.model.builder.StockMovementItemBuilder;
import org.openlmis.core.utils.DateUtil;
import org.robolectric.RuntimeEnvironment;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import roboguice.RoboGuice;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.openlmis.core.model.builder.StockCardBuilder.saveStockCardWithOneMovement;
@RunWith(LMISTestRunner.class)
public class StockRepositoryTest extends LMISRepositoryUnitTest {
StockRepository stockRepository;
ProductRepository productRepository;
LotRepository lotRepository;
Product product;
private ProgramRepository programRepository;
private ProductProgramRepository productProgramRepository;
private StockCard stockCard;
private StockMovementRepository stockMovementRepository;
@Before
public void setup() throws LMISException {
stockRepository = RoboGuice.getInjector(RuntimeEnvironment.application).getInstance(StockRepository.class);
productRepository = RoboGuice.getInjector(RuntimeEnvironment.application).getInstance(ProductRepository.class);
programRepository = RoboGuice.getInjector(RuntimeEnvironment.application).getInstance(ProgramRepository.class);
productProgramRepository = RoboGuice.getInjector(RuntimeEnvironment.application).getInstance(ProductProgramRepository.class);
lotRepository = RoboGuice.getInjector(RuntimeEnvironment.application).getInstance(LotRepository.class);
stockMovementRepository = RoboGuice.getInjector(RuntimeEnvironment.application).getInstance(StockMovementRepository.class);
saveTestProduct();
stockCard = new StockCard();
}
@Test
public void shouldCreateOrUpdateStockCard() throws LMISException {
StockCard stockCard = new StockCard();
stockCard.setStockOnHand(1);
stockCard.setProduct(product);
stockRepository.createOrUpdate(stockCard);
assertEquals(stockCard, stockRepository.list().get(0));
stockCard.setStockOnHand(10000);
stockRepository.createOrUpdate(stockCard);
assertEquals(stockCard, stockRepository.list().get(0));
}
@Test
public void shouldGetStockCardsBeforePeriodEndDate() throws Exception {
Program program1 = new ProgramBuilder().setProgramCode("code1").build();
Program program2 = new ProgramBuilder().setProgramCode("code2").setParentCode("code1").build();
Program program3 = new ProgramBuilder().setProgramCode("code3").build();
generateTestDataForGetStockCards("P1", true, false, program1, "1969-11-11");
generateTestDataForGetStockCards("P2", true, false, program1, "1970-11-11");
generateTestDataForGetStockCards("P3", true, false, program2, "1969-11-11");
generateTestDataForGetStockCards("P4", true, false, program3, "1969-11-11");
DateTime periodBegin = new DateTime(DateUtil.parseString("1970-01-01 10:10:10", DateUtil.DATE_TIME_FORMAT));
DateTime periodEnd = new DateTime(DateUtil.parseString("1970-02-21 10:10:10", DateUtil.DATE_TIME_FORMAT));
RnRForm rnRForm = RnRForm.init(program1, new Period(periodBegin, periodEnd), false);
List<StockCard> stockCardsBeforeTimeLine = stockRepository.getStockCardsBeforePeriodEnd(rnRForm);
assertThat(stockCardsBeforeTimeLine.size(), is(2));
assertThat(stockCardsBeforeTimeLine.get(0).getProduct().getCode(), is("P1"));
assertThat(stockCardsBeforeTimeLine.get(1).getProduct().getCode(), is("P3"));
}
@Test
public void shouldGetActiveAndNotArchivedStockCardsBeforePeriodEndDate() throws Exception {
Program program1 = new ProgramBuilder().setProgramCode("code1").build();
Program program2 = new ProgramBuilder().setProgramCode("code2").setParentCode("code1").build();
generateTestDataForGetStockCards("P1", true, false, program1, "1969-11-11");
generateTestDataForGetStockCards("P2", false, false, program1, "1969-11-11");
generateTestDataForGetStockCards("P3", true, false, program2, "1969-11-11");
generateTestDataForGetStockCards("P4", true, true, program2, "1969-11-11");
DateTime periodBegin = new DateTime(DateUtil.parseString("1970-01-01 10:10:10", DateUtil.DATE_TIME_FORMAT));
DateTime periodEnd = new DateTime(DateUtil.parseString("1970-02-21 10:10:10", DateUtil.DATE_TIME_FORMAT));
RnRForm rnRForm = RnRForm.init(program1, new Period(periodBegin, periodEnd), false);
List<StockCard> stockCardsBeforeTimeLine = stockRepository.getStockCardsBeforePeriodEnd(rnRForm);
assertThat(stockCardsBeforeTimeLine.size(), is(2));
assertThat(stockCardsBeforeTimeLine.get(0).getProduct().getCode(), is("P1"));
assertThat(stockCardsBeforeTimeLine.get(1).getProduct().getCode(), is("P3"));
}
private void generateTestDataForGetStockCards(String productCode, boolean isActive, boolean isArchived, Program program, String movementDate) throws LMISException {
Product product = new ProductBuilder().create().setCode(productCode).setIsActive(isActive).setIsArchived(isArchived).build();
productRepository.createOrUpdate(product);
programRepository.createOrUpdate(program);
createNewProductProgram(program.getProgramCode(), product.getCode());
StockCard stockCard = new StockCard();
stockCard.setProduct(product);
stockRepository.createOrUpdate(stockCard);
StockMovementItem stockMovementItem = new StockMovementItem();
stockMovementItem.setStockCard(stockCard);
stockMovementItem.setMovementDate(DateUtil.parseString(movementDate, "yyyy-MM-dd"));
stockCard.setStockOnHand(stockMovementItem.getStockOnHand());
stockRepository.addStockMovementAndUpdateStockCard(stockMovementItem);
stockRepository.refresh(stockCard);
}
private StockCard createNewStockCard(String code, String parentCode, Product product, boolean isEmergency) throws LMISException {
StockCard stockCard = new StockCard();
Program program = createNewProgram(code, parentCode, isEmergency);
programRepository.createOrUpdate(program);
productRepository.createOrUpdate(product);
ProductProgram productProgram = new ProductProgramBuilder()
.setProductCode(product.getCode())
.setProgramCode(program.getProgramCode())
.setActive(true).build();
productProgramRepository.createOrUpdate(productProgram);
stockCard.setProduct(product);
stockCard.setCreatedAt(new Date());
stockRepository.createOrUpdate(stockCard);
return stockCard;
}
private void createNewProductProgram(String code, String productCode) throws LMISException {
ProductProgram productProgram = new ProductProgramBuilder().setProgramCode(code).setProductCode(productCode).setActive(true).build();
productProgramRepository.createOrUpdate(productProgram);
}
@NonNull
private Program createNewProgram(String code, String parentCode, boolean isSupportEmergency) throws LMISException {
Program program = new ProgramBuilder().setProgramCode(code).setParentCode(parentCode).setSupportEmergency(isSupportEmergency).build();
programRepository.createOrUpdate(program);
return program;
}
@Test
public void shouldUpdateStockCardAndProduct() throws Exception {
StockCard stockCard = new StockCard();
product.setArchived(true);
stockCard.setProduct(product);
stockCard.setExpireDates("01/01/2016");
stockRepository.createOrUpdate(stockCard);
stockCard.setExpireDates("");
product.setArchived(false);
stockRepository.updateStockCardWithProduct(stockCard);
assertThat(stockCard.getExpireDates(), is(""));
}
@Test
public void shouldUpdateProductOfStockCard() throws Exception {
StockCard stockCard = new StockCard();
product.setArchived(true);
stockCard.setProduct(product);
stockCard.setExpireDates("01/01/2016");
stockRepository.createOrUpdate(stockCard);
stockCard.setExpireDates("");
product.setArchived(false);
stockRepository.updateProductOfStockCard(stockCard.getProduct());
assertThat(stockCard.getProduct().isArchived(), is(false));
}
@Test
public void shouldLoadEmergencyProducts() throws Exception {
//when
createNewStockCard("code", null, ProductBuilder.create().setCode("p1").setIsActive(true).setIsKit(false).build(), true);
createNewStockCard("otherCode", "parentCode", ProductBuilder.create().setCode("p2").setIsActive(true).setIsKit(false).build(), false);
Product product = ProductBuilder.buildAdultProduct();
product.setKit(true);
productRepository.createOrUpdate(product);
//then
List<StockCard> stockCardsBeforeTimeLine = stockRepository.listEmergencyStockCards();
assertThat(stockCardsBeforeTimeLine.size(), is(1));
}
private void saveTestProduct() throws LMISException {
product = new Product();
product.setPrimaryName("Test Product");
product.setStrength("200");
product.setCode("test code");
productRepository.createOrUpdate(product);
}
private StockMovementItem createMovementItem(MovementReasonManager.MovementType type, long quantity, StockCard stockCard, Date createdTime, Date movementDate, boolean synced) throws LMISException {
StockMovementItem stockMovementItem = new StockMovementItem();
stockMovementItem.setMovementQuantity(quantity);
stockMovementItem.setMovementType(type);
stockMovementItem.setMovementDate(movementDate);
stockMovementItem.setStockCard(stockCard);
stockMovementItem.setSynced(synced);
LMISTestApp.getInstance().setCurrentTimeMillis(createdTime.getTime());
if (stockMovementItem.isPositiveMovement()) {
stockMovementItem.setStockOnHand(stockCard.getStockOnHand() + quantity);
} else {
stockMovementItem.setStockOnHand(stockCard.getStockOnHand() - quantity);
}
stockCard.setStockOnHand(stockMovementItem.getStockOnHand());
stockRepository.addStockMovementAndUpdateStockCard(stockMovementItem);
stockRepository.refresh(stockCard);
return stockMovementItem;
}
@Test
public void shouldSaveStockCardAndBatchUpdateMovements() throws Exception {
Product product = ProductBuilder.create().setProductId(1L).setCode("p1").setIsActive(true).setIsKit(false).build();
productRepository.createOrUpdate(product);
StockCard stockCard = StockCardBuilder.buildStockCard();
stockCard.setProduct(product);
Lot lot1 = new Lot();
lot1.setProduct(product);
lot1.setExpirationDate(DateUtil.parseString("2017-12-31", DateUtil.DB_DATE_FORMAT));
lot1.setLotNumber("AAA");
LotOnHand lotOnHand1 = new LotOnHand();
lotOnHand1.setLot(lot1);
lotOnHand1.setStockCard(stockCard);
lotOnHand1.setQuantityOnHand(10L);
stockCard.setLotOnHandListWrapper(Arrays.asList(lotOnHand1));
StockMovementItem stockMovementItem = new StockMovementItemBuilder()
.withStockOnHand(200)
.withMovementType(MovementReasonManager.MovementType.RECEIVE)
.withMovementDate("2015-12-31")
.withQuantity(10)
.build();
stockMovementItem.setStockCard(stockCard);
LotMovementItem lotMovementItem = new LotMovementItemBuilder()
.setStockMovementItem(stockMovementItem)
.setLot(lot1)
.setMovementQuantity(2L)
.setStockOnHand(12L).build();
stockMovementItem.setLotMovementItemListWrapper(Arrays.asList(lotMovementItem));
stockCard.setStockMovementItemsWrapper(Arrays.asList(stockMovementItem));
stockRepository.saveStockCardAndBatchUpdateMovements(stockCard);
StockCard queriedStockCard = stockRepository.queryStockCardById(stockCard.getId());
assertThat(queriedStockCard.getProduct().getCode(), is(product.getCode()));
assertThat(queriedStockCard.getLotOnHandListWrapper().get(0).getQuantityOnHand(), is(10L));
StockMovementItem queriedStockMovementItem = queriedStockCard.getStockMovementItemsWrapper().get(0);
assertThat(queriedStockMovementItem.getMovementQuantity(), is(10L));
assertThat(queriedStockMovementItem.getLotMovementItemListWrapper().get(0).getMovementQuantity(), is(2L));
assertThat(queriedStockMovementItem.getLotMovementItemListWrapper().get(0).getLot().getLotNumber(), is("AAA"));
}
@Test
public void shouldDeleteDataOver13Months() throws Exception {
LMISTestApp.getInstance().setCurrentTimeMillis(DateUtil.parseString("2016-08-11", DateUtil.DB_DATE_FORMAT).getTime());
StockCard stockCard = saveStockCardWithOneMovement(stockRepository);
StockMovementItem stockMovementItem = createMovementItem(MovementReasonManager.MovementType.POSITIVE_ADJUST, 100L, stockCard, new Date(), new Date(), true);
Lot lot1 = new Lot();
lot1.setProduct(product);
lot1.setExpirationDate(DateUtil.parseString("2017-12-31", DateUtil.DB_DATE_FORMAT));
lot1.setLotNumber("AAA");
LotMovementItem lotMovementItem = new LotMovementItemBuilder()
.setStockMovementItem(stockMovementItem)
.setLot(lot1)
.setMovementQuantity(2L).build();
lotRepository.batchCreateLotsAndLotMovements(Arrays.asList(lotMovementItem));
stockRepository.deleteOldData();
StockCard stockCardQueried = stockRepository.queryStockCardById(stockCard.getId());
assertEquals(1, stockCardQueried.getStockMovementItemsWrapper().size());
assertEquals(MovementReasonManager.MovementType.POSITIVE_ADJUST, stockCardQueried.getStockMovementItemsWrapper().get(0).getMovementType());
assertEquals(1, stockCardQueried.getStockMovementItemsWrapper().get(0).getLotMovementItemListWrapper().size());
}
}