/*
* 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.content.Context;
import android.database.Cursor;
import com.google.inject.Inject;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.misc.TransactionManager;
import org.openlmis.core.LMISApp;
import org.openlmis.core.exceptions.LMISException;
import org.openlmis.core.manager.SharedPreferenceMgr;
import org.openlmis.core.model.LotOnHand;
import org.openlmis.core.model.Product;
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.persistence.DbUtil;
import org.openlmis.core.persistence.GenericDao;
import org.openlmis.core.persistence.LmisSqliteOpenHelper;
import org.openlmis.core.utils.DateUtil;
import org.roboguice.shaded.goole.common.base.Function;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import static org.roboguice.shaded.goole.common.collect.FluentIterable.from;
public class StockRepository {
@Inject
DbUtil dbUtil;
@Inject
Context context;
@Inject
ProductRepository productRepository;
@Inject
ProgramRepository programRepository;
@Inject
ProductProgramRepository productProgramRepository;
@Inject
LotRepository lotRepository;
@Inject
StockMovementRepository stockMovementRepository;
GenericDao<StockCard> genericDao;
@Inject
public StockRepository(Context context) {
genericDao = new GenericDao<>(StockCard.class, context);
}
public void batchSaveUnpackStockCardsWithMovementItemsAndUpdateProduct(final List<StockCard> stockCards) {
try {
dbUtil.withDaoAsBatch(StockCard.class, new DbUtil.Operation<StockCard, Object>() {
@Override
public Object operate(Dao<StockCard, String> dao) throws SQLException, LMISException {
for (StockCard stockCard : stockCards) {
dao.createOrUpdate(stockCard);
updateProductOfStockCard(stockCard.getProduct());
stockMovementRepository.batchCreateOrUpdateStockMovementsAndLotInfo(stockCard.getStockMovementItemsWrapper());
}
return null;
}
});
} catch (LMISException e) {
e.reportToFabric();
}
}
public void createOrUpdate(final StockCard stockCard) {
try {
genericDao.createOrUpdate(stockCard);
} catch (LMISException e) {
e.reportToFabric();
}
}
public void refresh(StockCard stockCard) {
try {
genericDao.refresh(stockCard);
} catch (LMISException e) {
e.reportToFabric();
}
}
protected void saveStockCardAndBatchUpdateMovements(final StockCard stockCard) {
try {
TransactionManager.callInTransaction(LmisSqliteOpenHelper.getInstance(context).getConnectionSource(), new Callable<Object>() {
@Override
public Object call() throws Exception {
createOrUpdate(stockCard);
lotRepository.createOrUpdateLotsInformation(stockCard.getLotOnHandListWrapper());
stockMovementRepository.batchCreateOrUpdateStockMovementsAndLotMovements(stockCard.getStockMovementItemsWrapper());
return null;
}
});
} catch (SQLException e) {
new LMISException(e).reportToFabric();
}
}
public void updateProductOfStockCard(Product product) {
try {
productRepository.updateProduct(product);
} catch (LMISException e) {
e.reportToFabric();
}
}
public void createOrUpdateStockCardWithStockMovement(final StockCard stockCard) {
try {
TransactionManager.callInTransaction(LmisSqliteOpenHelper.getInstance(context).getConnectionSource(), new Callable<Object>() {
@Override
public Object call() throws Exception {
createOrUpdate(stockCard);
updateProductOfStockCard(stockCard.getProduct());
stockMovementRepository.batchCreateStockMovementItemAndLotItems(stockCard.generateInitialStockMovementItem());
return null;
}
});
} catch (SQLException e) {
new LMISException(e).reportToFabric();
}
}
public void addStockMovementAndUpdateStockCard(final StockMovementItem stockMovementItem) {
try {
TransactionManager.callInTransaction(LmisSqliteOpenHelper.getInstance(context).getConnectionSource(), new Callable<Object>() {
@Override
public Object call() throws Exception {
StockCard stockcard = stockMovementItem.getStockCard();
createOrUpdate(stockcard);
stockMovementRepository.batchCreateStockMovementItemAndLotItems(stockMovementItem);
return null;
}
});
} catch (SQLException e) {
new LMISException(e).reportToFabric();
}
}
public List<StockCard> list() {
try {
List<StockCard> stockCards = genericDao.queryForAll();
Collections.sort(stockCards);
return stockCards;
} catch (LMISException e) {
e.reportToFabric();
}
return null;
}
public boolean hasStockData() {
List<StockCard> list = list();
return list != null && list.size() > 0;
}
public boolean hasOldDate() {
Date dueDateShouldDataLivedInDB = DateUtil.dateMinusMonth(new Date(), SharedPreferenceMgr.getInstance().getMonthOffsetThatDefinedOldData());
List<StockCard> list = list();
if (hasStockData()) {
for (StockCard stockCard : list) {
for (StockMovementItem stockMovementItem : stockCard.getStockMovementItemsWrapper()) {
if (stockMovementItem.getMovementDate().before(dueDateShouldDataLivedInDB)) {
return true;
}
}
}
}
return false;
}
private List<StockCard> listStockCardsByProductIds(final List<Long> productIds) throws LMISException {
return dbUtil.withDao(StockCard.class, new DbUtil.Operation<StockCard, List<StockCard>>() {
@Override
public List<StockCard> operate(Dao<StockCard, String> dao) throws SQLException, LMISException {
return dao.queryBuilder().where().in("product_id", productIds).query();
}
});
}
public List<StockCard> listEmergencyStockCards() throws LMISException {
List<Program> programs = programRepository.listEmergencyPrograms();
List<String> programCodes = from(programs).transform(new Function<Program, String>() {
@Override
public String apply(Program program) {
return program.getProgramCode();
}
}).toList();
List<Long> productIds = productProgramRepository.queryActiveProductIdsByProgramsWithKits(programCodes, false);
return listStockCardsByProductIds(productIds);
}
public StockCard queryStockCardById(final long id) throws LMISException {
return genericDao.getById(String.valueOf(id));
}
public StockCard queryStockCardByProductId(final long productId) throws LMISException {
return dbUtil.withDao(StockCard.class, new DbUtil.Operation<StockCard, StockCard>() {
@Override
public StockCard operate(Dao<StockCard, String> dao) throws SQLException {
return dao.queryBuilder().where().eq("product_id", productId).queryForFirst();
}
});
}
public void updateStockCardWithProduct(final StockCard stockCard) throws LMISException {
dbUtil.withDaoAsBatch(StockCard.class, new DbUtil.Operation<StockCard, Object>() {
@Override
public Object operate(Dao<StockCard, String> dao) throws SQLException, LMISException {
dao.update(stockCard);
updateProductOfStockCard(stockCard.getProduct());
return null;
}
});
}
protected List<StockCard> getStockCardsBeforePeriodEnd(RnRForm rnRForm) throws LMISException {
String rawSql = "SELECT * FROM stock_cards WHERE product_id IN ("
+ " SELECT id FROM products WHERE isActive =1 AND isArchived = 0 AND code IN ("
+ " SELECT productCode FROM product_programs WHERE isActive=1 AND programCode IN ("
+ " SELECT programCode FROM programs WHERE parentCode= '" + rnRForm.getProgram().getProgramCode() + "'"
+ " OR programCode='" + rnRForm.getProgram().getProgramCode() + "')))"
+ " AND id NOT IN ("
+ " SELECT stockCard_id FROM stock_items WHERE stockCard_id NOT IN ("
+ " SELECT stockCard_id FROM stock_items"
+ " WHERE movementDate <= '" + DateUtil.formatDateTime(rnRForm.getPeriodEnd()) + "'"
+ " AND createdTime <= '" + DateUtil.formatDateTime(rnRForm.getPeriodEnd()) + "'))";
final Cursor cursor = LmisSqliteOpenHelper.getInstance(LMISApp.getContext()).getWritableDatabase().rawQuery(rawSql, null);
List<StockCard> stockCardList = new ArrayList<>();
if (cursor.moveToFirst()) {
do {
StockCard stockCard = new StockCard();
stockCard.setProduct(productRepository.getProductById(cursor.getLong(cursor.getColumnIndexOrThrow("product_id"))));
stockCard.setStockOnHand(cursor.getLong(cursor.getColumnIndexOrThrow("stockOnHand")));
stockCard.setAvgMonthlyConsumption(cursor.getFloat(cursor.getColumnIndexOrThrow("avgMonthlyConsumption")));
stockCard.setId(cursor.getLong(cursor.getColumnIndexOrThrow("id")));
stockCard.setLotOnHandListWrapper(getLotOnHandByStockCard(stockCard.getId()));
stockCardList.add(stockCard);
} while (cursor.moveToNext());
}
if (!cursor.isClosed()) {
cursor.close();
}
return stockCardList;
}
private List<LotOnHand> getLotOnHandByStockCard(final long stockCardId) throws LMISException {
return dbUtil.withDao(LotOnHand.class, new DbUtil.Operation<LotOnHand, List<LotOnHand>>() {
@Override
public List<LotOnHand> operate(Dao<LotOnHand, String> dao) throws SQLException, LMISException {
return dao.queryBuilder()
.where()
.eq("stockCard_id", stockCardId)
.query();
}
});
}
public void batchCreateSyncDownStockCardsAndMovements(final List<StockCard> stockCards) {
try {
TransactionManager.callInTransaction(LmisSqliteOpenHelper.getInstance(context).getConnectionSource(), new Callable<Object>() {
@Override
public Object call() throws Exception {
for (StockCard stockCard : stockCards) {
if (stockCard.getId() <= 0) {
saveStockCardAndBatchUpdateMovements(stockCard);
} else {
stockMovementRepository.batchCreateOrUpdateStockMovementsAndLotMovements(stockCard.getStockMovementItemsWrapper());
}
}
return null;
}
});
} catch (SQLException e) {
new LMISException(e).reportToFabric();
}
}
public void deleteOldData() {
String dueDateShouldDataLivedInDB = DateUtil.formatDate(DateUtil.dateMinusMonth(new Date(), SharedPreferenceMgr.getInstance().getMonthOffsetThatDefinedOldData()), DateUtil.DB_DATE_FORMAT);
String rawSqlDeleteLotItems = "DELETE FROM lot_movement_items "
+ "WHERE StockMovementItem_id IN (SELECT id FROM stock_items WHERE movementDate < '" + dueDateShouldDataLivedInDB + "' );";
String rawSqlDeleteStockMovementItems = "DELETE FROM stock_items "
+ "WHERE movementDate < '" + dueDateShouldDataLivedInDB + "'; ";
LmisSqliteOpenHelper.getInstance(LMISApp.getContext()).getWritableDatabase().execSQL(rawSqlDeleteLotItems);
LmisSqliteOpenHelper.getInstance(LMISApp.getContext()).getWritableDatabase().execSQL(rawSqlDeleteStockMovementItems);
}
}