/*
* 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.presenter;
import android.support.annotation.NonNull;
import com.google.inject.Inject;
import org.joda.time.DateTime;
import org.openlmis.core.LMISApp;
import org.openlmis.core.R;
import org.openlmis.core.exceptions.LMISException;
import org.openlmis.core.exceptions.ViewNotMatchException;
import org.openlmis.core.manager.SharedPreferenceMgr;
import org.openlmis.core.model.Inventory;
import org.openlmis.core.model.Period;
import org.openlmis.core.model.RnRForm;
import org.openlmis.core.model.SyncError;
import org.openlmis.core.model.SyncType;
import org.openlmis.core.model.repository.InventoryRepository;
import org.openlmis.core.model.repository.RnrFormRepository;
import org.openlmis.core.model.repository.StockMovementRepository;
import org.openlmis.core.model.repository.StockRepository;
import org.openlmis.core.model.repository.SyncErrorsRepository;
import org.openlmis.core.model.service.RequisitionPeriodService;
import org.openlmis.core.view.BaseView;
import org.openlmis.core.view.viewmodel.RnRFormViewModel;
import org.roboguice.shaded.goole.common.base.Function;
import org.roboguice.shaded.goole.common.collect.FluentIterable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import lombok.Setter;
import rx.Observable;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
public class RnRFormListPresenter extends Presenter {
@Inject
RnrFormRepository repository;
@Inject
InventoryRepository inventoryRepository;
@Inject
SyncErrorsRepository syncErrorsRepository;
@Inject
StockRepository stockRepository;
@Setter
String programCode;
@Inject
SharedPreferenceMgr sharedPreferenceMgr;
@Inject
RequisitionPeriodService requisitionPeriodService;
@Inject
private StockMovementRepository stockMovementRepository;
@Override
public void attachView(BaseView v) throws ViewNotMatchException {
}
public Observable<List<RnRFormViewModel>> loadRnRFormList() {
return Observable.create(new Observable.OnSubscribe<List<RnRFormViewModel>>() {
@Override
public void call(Subscriber<? super List<RnRFormViewModel>> subscriber) {
try {
subscriber.onNext(buildFormListViewModels());
subscriber.onCompleted();
} catch (LMISException e) {
subscriber.onError(e);
}
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
}
@NonNull
protected List<RnRFormViewModel> buildFormListViewModels() throws LMISException {
List<RnRFormViewModel> rnRFormViewModels = new ArrayList<>();
List<RnRForm> rnRForms = repository.listInclude(RnRForm.Emergency.Yes, programCode);
generateRnrViewModelByRnrFormsInDB(rnRFormViewModels, rnRForms);
generateViewModelsByCurrentDate(rnRFormViewModels);
populateSyncErrorsOnViewModels(rnRFormViewModels);
Collections.sort(rnRFormViewModels, new Comparator<RnRFormViewModel>() {
@Override
public int compare(RnRFormViewModel lhs, RnRFormViewModel rhs) {
return rhs.getPeriodEndMonth().compareTo(lhs.getPeriodEndMonth());
}
});
return rnRFormViewModels;
}
private void generateViewModelsByCurrentDate(List<RnRFormViewModel> rnRFormViewModels) throws LMISException {
if (requisitionPeriodService.hasMissedPeriod(programCode)) {
addPreviousPeriodMissedViewModels(rnRFormViewModels);
} else {
Period nextPeriodInSchedule = requisitionPeriodService.generateNextPeriod(programCode, null);
if (isAllRnrFormInDBCompletedOrNoRnrFormInDB()) {
rnRFormViewModels.add(generateRnrFormViewModelWithoutRnrForm(nextPeriodInSchedule));
}
}
}
private RnRFormViewModel generateRnrFormViewModelWithoutRnrForm(Period currentPeriod) throws LMISException {
if (isCanNotCreateRnr(currentPeriod)) {
return new RnRFormViewModel(currentPeriod, programCode, RnRFormViewModel.TYPE_CANNOT_DO_MONTHLY_INVENTORY);
}
if (LMISApp.getInstance().getFeatureToggleFor(R.bool.feature_training)) {
if (stockMovementRepository.queryStockMovementDatesByProgram(programCode).isEmpty()) {
return new RnRFormViewModel(currentPeriod, programCode, RnRFormViewModel.TYPE_CANNOT_DO_MONTHLY_INVENTORY);
}
}
List<Inventory> physicalInventories = inventoryRepository.queryPeriodInventory(currentPeriod);
if (physicalInventories == null || physicalInventories.size() == 0) {
return new RnRFormViewModel(currentPeriod, programCode, RnRFormViewModel.TYPE_UNCOMPLETE_INVENTORY_IN_CURRENT_PERIOD);
} else {
return new RnRFormViewModel(currentPeriod, programCode, RnRFormViewModel.TYPE_INVENTORY_DONE);
}
}
private boolean isCanNotCreateRnr(Period currentPeriod) {
DateTime dateTime = new DateTime(LMISApp.getInstance().getCurrentTimeMillis());
return dateTime.isBefore(currentPeriod.getInventoryBegin());
}
private void populateSyncErrorsOnViewModels(final List<RnRFormViewModel> rnrViewModels) {
for (RnRFormViewModel rnrViewModel : rnrViewModels) {
rnrViewModel.setSyncServerErrorMessage(getRnrFormSyncError(rnrViewModel.getId()));
}
}
private String getRnrFormSyncError(long rnrId) {
List<SyncError> syncErrorList = syncErrorsRepository.getBySyncTypeAndObjectId(SyncType.RnRForm, rnrId);
if (null == syncErrorList || syncErrorList.isEmpty())
return null;
return syncErrorList.get(syncErrorList.size() - 1).getErrorMessage();
}
protected void generateRnrViewModelByRnrFormsInDB(List<RnRFormViewModel> viewModels, List<RnRForm> rnRForms) {
viewModels.addAll(FluentIterable.from(rnRForms).transform(new Function<RnRForm, RnRFormViewModel>() {
@Override
public RnRFormViewModel apply(RnRForm form) {
return form.isEmergency() ? RnRFormViewModel.buildEmergencyViewModel(form) : RnRFormViewModel.buildNormalRnrViewModel(form);
}
}).toList());
}
protected void addPreviousPeriodMissedViewModels(List<RnRFormViewModel> viewModels) throws LMISException {
int offsetMonth = requisitionPeriodService.getMissedPeriodOffsetMonth(this.programCode);
DateTime inventoryBeginDate = requisitionPeriodService.getCurrentMonthInventoryBeginDate();
for (int i = 0; i < offsetMonth; i++) {
viewModels.add(RnRFormViewModel.buildMissedPeriod(inventoryBeginDate.toDate(), inventoryBeginDate.plusMonths(1).toDate()));
inventoryBeginDate = inventoryBeginDate.minusMonths(1);
}
generateFirstMissedRnrFormViewModel(viewModels, offsetMonth, inventoryBeginDate);
}
private void generateFirstMissedRnrFormViewModel(List<RnRFormViewModel> viewModels, int offsetMonth, DateTime inventoryBeginDate) throws LMISException {
if (isAllRnrFormInDBCompletedOrNoRnrFormInDB()) {
addFirstMissedAndNotPendingRnrForm(viewModels);
} else {
viewModels.add(RnRFormViewModel.buildMissedPeriod(inventoryBeginDate.toDate(), inventoryBeginDate.plusMonths(1).toDate()));
}
}
private void addFirstMissedAndNotPendingRnrForm(List<RnRFormViewModel> viewModels) throws LMISException {
Period nextPeriodInSchedule = requisitionPeriodService.generateNextPeriod(programCode, null);
List<Inventory> physicalInventories = inventoryRepository.queryPeriodInventory(nextPeriodInSchedule);
if (physicalInventories.isEmpty()) {
viewModels.add(RnRFormViewModel.buildFirstMissedPeriod(programCode, nextPeriodInSchedule.getBegin().toDate(), nextPeriodInSchedule.getEnd().toDate()));
} else {
viewModels.add(new RnRFormViewModel(nextPeriodInSchedule, programCode, RnRFormViewModel.TYPE_INVENTORY_DONE));
}
}
public void deleteRnRForm(RnRForm form) throws LMISException {
repository.removeRnrForm(form);
}
private boolean isAllRnrFormInDBCompletedOrNoRnrFormInDB() throws LMISException {
List<RnRForm> rnRForms = repository.listInclude(RnRForm.Emergency.No, programCode);
return rnRForms.isEmpty() || rnRForms.get(rnRForms.size() - 1).getStatus() == RnRForm.STATUS.AUTHORIZED;
}
public Observable<Boolean> hasMissedPeriod() {
return Observable.create(new Observable.OnSubscribe<Boolean>() {
@Override
public void call(Subscriber<? super Boolean> subscriber) {
try {
subscriber.onNext(requisitionPeriodService.hasMissedPeriod(programCode));
subscriber.onCompleted();
} catch (LMISException e) {
e.reportToFabric();
subscriber.onError(e);
}
}
}).observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io());
}
}