/*
* 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;
import com.google.gson.annotations.Expose;
import com.j256.ormlite.dao.ForeignCollection;
import com.j256.ormlite.field.DataType;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.field.ForeignCollectionField;
import com.j256.ormlite.table.DatabaseTable;
import org.openlmis.core.manager.MovementReasonManager;
import org.openlmis.core.utils.DateUtil;
import org.openlmis.core.utils.ListUtil;
import org.openlmis.core.view.viewmodel.InventoryViewModel;
import org.openlmis.core.view.viewmodel.LotMovementViewModel;
import org.roboguice.shaded.goole.common.base.Function;
import org.roboguice.shaded.goole.common.collect.FluentIterable;
import org.roboguice.shaded.goole.common.collect.ImmutableList;
import java.util.Date;
import java.util.List;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Getter
@Setter
@NoArgsConstructor
@DatabaseTable(tableName = "stock_items")
public class StockMovementItem extends BaseModel {
@Expose
@DatabaseField
String documentNumber;
@Expose
@DatabaseField
long movementQuantity;
@Expose
@DatabaseField
Long requested;
@DatabaseField
String reason;
@DatabaseField
MovementReasonManager.MovementType movementType;
@DatabaseField(foreign = true, foreignAutoRefresh = true)
StockCard stockCard;
@DatabaseField
long stockOnHand;
@Expose
@DatabaseField
String signature;
@Expose
String expireDates;
@DatabaseField(canBeNull = false, dataType = DataType.DATE_STRING, format = DateUtil.DB_DATE_FORMAT)
private java.util.Date movementDate;
@DatabaseField
private boolean synced = false;
@DatabaseField(canBeNull = false, dataType = DataType.DATE_STRING, format = DateUtil.DATE_TIME_FORMAT)
private java.util.Date createdTime;
@ForeignCollectionField()
private ForeignCollection<LotMovementItem> foreignLotMovementItems;
private List<LotMovementItem> lotMovementItemListWrapper;
private List<LotMovementItem> newAddedLotMovementItemListWrapper;
public StockMovementItem(StockCard stockCard, InventoryViewModel model) {
this.stockCard = stockCard;
this.movementDate = new Date();
this.reason = MovementReasonManager.INVENTORY;
this.movementType = MovementReasonManager.MovementType.PHYSICAL_INVENTORY;
populateLotQuantitiesAndCalculateNewSOH(model.getNewLotMovementViewModelList(), movementType);
}
public boolean isPositiveMovement() {
return movementType.equals(MovementReasonManager.MovementType.RECEIVE) || movementType.equals(MovementReasonManager.MovementType.POSITIVE_ADJUST);
}
public boolean isNegativeMovement() {
return movementType.equals(MovementReasonManager.MovementType.ISSUE) || movementType.equals(MovementReasonManager.MovementType.NEGATIVE_ADJUST);
}
public Period getMovementPeriod() {
return Period.of(movementDate);
}
public long calculatePreviousSOH() {
if (isNegativeMovement()) {
return stockOnHand + movementQuantity;
} else if (isPositiveMovement()) {
return stockOnHand - movementQuantity;
} else {
return stockOnHand;
}
}
public StockMovementItem(StockCard stockCard) {
this.stockCard = stockCard;
this.stockOnHand = stockCard.getStockOnHand();
this.movementDate = new Date();
}
public List<LotMovementItem> getLotMovementItemListWrapper() {
lotMovementItemListWrapper = ListUtil.wrapOrEmpty(foreignLotMovementItems, lotMovementItemListWrapper);
return lotMovementItemListWrapper;
}
public List<LotMovementItem> getNewAddedLotMovementItemListWrapper() {
newAddedLotMovementItemListWrapper = ListUtil.wrapOrEmpty(foreignLotMovementItems, newAddedLotMovementItemListWrapper);
return newAddedLotMovementItemListWrapper;
}
public void populateLotQuantitiesAndCalculateNewSOH(List<LotMovementViewModel> lotMovementViewModelList, MovementReasonManager.MovementType movementType) {
final StockMovementItem stockMovementItem = this;
if (!lotMovementViewModelList.isEmpty()) {
setLotMovementItemListWrapper(FluentIterable.from(lotMovementViewModelList).transform(new Function<LotMovementViewModel, LotMovementItem>() {
@Override
public LotMovementItem apply(LotMovementViewModel lotMovementViewModel) {
LotMovementItem lotItem = lotMovementViewModel.convertViewToModel(getStockCard().getProduct());
lotItem.setStockMovementItemAndUpdateMovementQuantity(stockMovementItem);
return lotItem;
}
}).toList());
long movementQuantity = 0;
for (LotMovementViewModel lotMovementViewModel : lotMovementViewModelList) {
movementQuantity += Long.parseLong(lotMovementViewModel.getQuantity());
}
setMovementQuantity(movementQuantity);
if (movementType.equals(MovementReasonManager.MovementType.ISSUE) || movementType.equals(MovementReasonManager.MovementType.NEGATIVE_ADJUST)) {
setStockOnHand(getStockOnHand() - movementQuantity);
} else {
setStockOnHand(getStockOnHand() + movementQuantity);
}
}
}
public void populateLotAndResetStockOnHandOfLotAccordingPhysicalAdjustment(List<LotMovementViewModel> existingLotMovementList, List<LotMovementViewModel> newAddedLotMovementList) {
final StockMovementItem stockMovementItem = this;
ImmutableList<LotMovementItem> existingLotMovementItemList = FluentIterable.from(existingLotMovementList).transform(new Function<LotMovementViewModel, LotMovementItem>() {
@Override
public LotMovementItem apply(LotMovementViewModel lotMovementViewModel) {
LotMovementItem lotItem = lotMovementViewModel.convertViewToModelAndResetSOH(getStockCard().getProduct());
lotItem.setStockMovementItem(stockMovementItem);
lotItem.setStockOnHandReset(true);
return lotItem;
}
}).toList();
ImmutableList<LotMovementItem> newAddedLotMovementItemList = FluentIterable.from(newAddedLotMovementList).transform(new Function<LotMovementViewModel, LotMovementItem>() {
@Override
public LotMovementItem apply(LotMovementViewModel lotMovementViewModel) {
lotMovementViewModel.setMovementType(MovementReasonManager.MovementType.PHYSICAL_INVENTORY);
LotMovementItem lotItem = lotMovementViewModel.convertViewToModel(getStockCard().getProduct());
lotItem.setStockMovementItem(stockMovementItem);
return lotItem;
}
}).toList();
setLotMovementItemListWrapper(existingLotMovementItemList);
setNewAddedLotMovementItemListWrapper(newAddedLotMovementItemList);
}
}