/*
* This program is part of the OpenLMIS logistics management information system platform software.
* Copyright © 2013 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 info@OpenLMIS.org.
*/
package org.openlmis.order.service;
import lombok.NoArgsConstructor;
import org.apache.commons.collections.Transformer;
import org.openlmis.core.domain.*;
import org.openlmis.core.repository.OrderConfigurationRepository;
import org.openlmis.core.repository.helper.CommaSeparator;
import org.openlmis.core.service.*;
import org.openlmis.fulfillment.shared.FulfillmentPermissionService;
import org.openlmis.order.domain.DateFormat;
import org.openlmis.order.domain.Order;
import org.openlmis.order.domain.OrderStatus;
import org.openlmis.order.dto.OrderFileTemplateDTO;
import org.openlmis.order.repository.OrderRepository;
import org.openlmis.rnr.domain.Rnr;
import org.openlmis.rnr.domain.RnrLineItem;
import org.openlmis.rnr.service.RequisitionService;
import org.openlmis.shipment.domain.ShipmentFileInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import static java.util.Arrays.asList;
import static java.util.Collections.sort;
import static org.apache.commons.collections.CollectionUtils.collect;
import static org.apache.commons.collections4.ListUtils.emptyIfNull;
import static org.apache.commons.lang.ArrayUtils.contains;
import static org.openlmis.core.domain.RightName.FACILITY_FILL_SHIPMENT;
import static org.openlmis.core.domain.RightName.MANAGE_POD;
import static org.openlmis.order.domain.OrderStatus.*;
/**
* Exposes the services for handling Order entity.
*/
@Service
@NoArgsConstructor
public class OrderService {
@Autowired
private OrderConfigurationRepository orderConfigurationRepository;
@Autowired
private OrderRepository orderRepository;
@Autowired
private RequisitionService requisitionService;
@Autowired
private SupplyLineService supplyLineService;
@Autowired
private OrderEventService orderEventService;
@Autowired
RoleAssignmentService roleAssignmentService;
@Autowired
FulfillmentPermissionService fulfillmentPermissionService;
@Autowired
private ProgramService programService;
@Autowired
private StatusChangeEventService statusChangeEventService;
@Autowired
private UserService userService;
@Autowired
private FacilityService facilityService;
@Autowired
private CommaSeparator commaSeparator;
public static String SUPPLY_LINE_MISSING_COMMENT = "order.ftpComment.supplyline.missing";
private Integer pageSize;
@Autowired
public void setPageSize(@Value("${order.page.size}") String pageSize) {
this.pageSize = Integer.parseInt(pageSize);
}
@Transactional
public void convertToOrder(List<Rnr> rnrList, Long userId) {
requisitionService.releaseRequisitionsAsOrder(rnrList, userId);
Order order;
for (Rnr rnr : rnrList) {
Long depotId = rnr.getSupplyingDepotId();
rnr = requisitionService.getLWById(rnr.getId());
rnr.setModifiedBy(userId);
order = new Order(rnr);
SupplyLine supplyLine = supplyLineService.getSupplyLineBy(new SupervisoryNode(rnr.getSupervisoryNodeId()), rnr.getProgram());
if (depotId != null && supplyLine.getSupplyingFacility().getId() != depotId) {
supplyLine = supplyLineService.getByFacilityProgram(depotId, rnr.getProgram().getId());
}
order.setSupplyLine(supplyLine);
if (!fulfillmentPermissionService.hasPermissionOnWarehouse(userId, supplyLine.getSupplyingFacility().getId(), RightName.CONVERT_TO_ORDER)) {
throw new AccessDeniedException("user.not.authorized");
}
OrderStatus status;
if (order.getSupplyLine() == null) {
status = TRANSFER_FAILED;
order.setFtpComment(SUPPLY_LINE_MISSING_COMMENT);
} else {
status = order.getSupplyLine().getExportOrders() ? IN_ROUTE : READY_TO_PACK;
}
order.setStatus(status);
order.setOrderNumber(getOrderNumberConfiguration().getOrderNumberFor(rnr.getId(), programService.getById(rnr.getProgram().getId()), rnr.isEmergency()));
orderRepository.save(order);
order.setRnr(requisitionService.getFullRequisitionById(order.getRnr().getId()));
sendOrderStatusChangeMail(order);
orderEventService.notifyForStatusChange(order);
}
}
private void sendOrderStatusChangeMail(Order order) {
Rnr requisition = order.getRnr();
List<User> usersWithRight = new ArrayList<>();
Facility supplyingFacility = order.getSupplyLine().getSupplyingFacility();
if (order.getStatus().equals(READY_TO_PACK)) {
usersWithRight = userService.getUsersWithRightOnWarehouse(supplyingFacility.getId(), FACILITY_FILL_SHIPMENT);
} else if (order.getStatus().equals(PACKED)) {
usersWithRight = userService.getUsersWithRightOnWarehouse(supplyingFacility.getId(), MANAGE_POD);
}
ArrayList<User> activeUsersWithRight = userService.filterForActiveUsers(usersWithRight);
statusChangeEventService.notifyUsers(activeUsersWithRight, null, requisition.getFacility(),
requisition.getProgram(), requisition.getPeriod(), order.getStatus().toString());
}
public List<Order> getOrdersForPage(int page, Long userId, String rightName) {
List<Order> orders = orderRepository.getOrdersForPage(page, pageSize, userId, rightName);
return fillOrders(orders);
}
public List<Order> getOrdersForPage(int page, Long userId, String right, Long supplyDepot, Long program, Long period) {
List<Order> orders = orderRepository.getOrdersForPage(page, pageSize, userId, right, supplyDepot, program, period);
return fillOrders(orders);
}
public Order getOrder(Long id) {
Order order = orderRepository.getById(id);
if (order == null) {
return null;
}
Rnr requisition = requisitionService.getFullRequisitionById(order.getRnr().getId());
requisition.setProgram(programService.getById(requisition.getProgram().getId()));
removeUnorderedProducts(requisition);
order.setRnr(requisition);
return order;
}
private void removeUnorderedProducts(Rnr requisition) {
List<RnrLineItem> fullSupplyLineItems = requisition.getFullSupplyLineItems();
requisition.setFullSupplyLineItems(getLineItemsForOrder(fullSupplyLineItems));
List<RnrLineItem> nonFullSupplyLineItems = requisition.getNonFullSupplyLineItems();
requisition.setNonFullSupplyLineItems(getLineItemsForOrder(nonFullSupplyLineItems));
}
public void updateStatusAndShipmentIdForOrders(Set<String> orderNumbers, ShipmentFileInfo shipmentFileInfo) {
for (String orderNumber : orderNumbers) {
OrderStatus status = (shipmentFileInfo.isProcessingError()) ? RELEASED : PACKED;
orderRepository.updateStatusAndShipmentIdForOrder(orderNumber, status, shipmentFileInfo.getId());
Order order = orderRepository.getByOrderNumber(orderNumber);
order.setRnr(requisitionService.getFullRequisitionById(order.getRnr().getId()));
sendOrderStatusChangeMail(order);
orderEventService.notifyForStatusChange(order);
}
}
public OrderFileTemplateDTO getOrderFileTemplateDTO() {
return new OrderFileTemplateDTO(orderConfigurationRepository.getConfiguration(), orderRepository.getOrderFileTemplate());
}
@Transactional
public void saveOrderFileTemplate(OrderFileTemplateDTO orderFileTemplateDTO, Long userId) {
OrderConfiguration orderConfiguration = orderFileTemplateDTO.getOrderConfiguration();
orderConfiguration.setModifiedBy(userId);
orderConfigurationRepository.update(orderConfiguration);
orderRepository.saveOrderFileColumns(orderFileTemplateDTO.getOrderFileColumns(), userId);
}
public OrderNumberConfiguration getOrderNumberConfiguration() {
return orderConfigurationRepository.getOrderNumberConfiguration();
}
@Transactional
public void updateOrderNumberConfiguration(OrderNumberConfiguration orderNumberConfiguration) {
orderNumberConfiguration.validate();
orderConfigurationRepository.updateOrderNumberConfiguration(orderNumberConfiguration);
}
public Set<DateFormat> getAllDateFormats() {
TreeSet<DateFormat> dateFormats = new TreeSet<>();
dateFormats.addAll(asList(DateFormat.values()));
return dateFormats;
}
public void updateOrderStatus(Order order) {
orderRepository.updateOrderStatus(order);
order.setRnr(requisitionService.getFullRequisitionById(order.getId()));
orderEventService.notifyForStatusChange(order);
}
public boolean isShippable(String orderNumber) {
return hasStatus(orderNumber, RELEASED);
}
public Integer getNumberOfPages() {
return orderRepository.getNumberOfPages(pageSize);
}
public Integer getNumberOfPages(Long supplyDepot, Long program, Long period) {
return orderRepository.getNumberOfPages(pageSize, supplyDepot, program, period);
}
public Integer getPageSize() {
return pageSize;
}
public List<Order> searchByStatusAndRight(Long userId, List<OrderStatus> statuses, Long programId, Long facilityId) {
List<FulfillmentRoleAssignment> fulfilmentRolesWithRight = roleAssignmentService.getFulfilmentRolesWithRight(userId, RightName.MANAGE_POD);
List<Facility> managedFacilities = new ArrayList<>();
managedFacilities.addAll(emptyIfNull(facilityService.getUserSupervisedFacilities(userId, programId, RightName.COMPLETE_POD)));
Facility homeFacility = facilityService.getHomeFacilityForRights(userId, programId, RightName.COMPLETE_POD);
if (homeFacility != null) {
managedFacilities.add(homeFacility);
}
String managedFacilityIds = commaSeparator.commaSeparateIds(managedFacilities);
List<Order> orders = orderRepository.searchByWarehousesAndStatuses((List<Long>) collect(fulfilmentRolesWithRight, new Transformer() {
@Override
public Object transform(Object o) {
return ((FulfillmentRoleAssignment) o).getFacilityId();
}
}), statuses, programId, facilityId, managedFacilityIds);
orders = fillOrders(orders);
sort(orders);
return orders;
}
private List<Order> fillOrders(List<Order> orders) {
Rnr rnr;
for (Order order : orders) {
rnr = requisitionService.getFullRequisitionById(order.getRnr().getId());
removeUnorderedProducts(rnr);
order.setRnr(rnr);
}
return orders;
}
private List<RnrLineItem> getLineItemsForOrder(List<RnrLineItem> rnrLineItems) {
List<RnrLineItem> lineItemsForOrder = new ArrayList<>();
for (RnrLineItem rnrLineItem : rnrLineItems) {
if (rnrLineItem.getPacksToShip() != null && rnrLineItem.getPacksToShip() > 0) {
lineItemsForOrder.add(rnrLineItem);
}
}
return lineItemsForOrder;
}
public boolean hasStatus(String orderNumber, OrderStatus... statuses) {
return contains(statuses, orderRepository.getStatus(orderNumber));
}
public Order getByOrderNumber(String orderNumber) {
return orderRepository.getByOrderNumber(orderNumber);
}
}