package com.qcadoo.mes.materialFlowResources.service;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.qcadoo.mes.materialFlowResources.constants.DocumentFields;
import com.qcadoo.mes.materialFlowResources.constants.DocumentState;
import com.qcadoo.mes.materialFlowResources.constants.DocumentType;
import com.qcadoo.mes.materialFlowResources.constants.MaterialFlowResourcesConstants;
import com.qcadoo.mes.materialFlowResources.constants.ParameterFieldsMFR;
import com.qcadoo.mes.materialFlowResources.constants.PositionFields;
import com.qcadoo.mes.materialFlowResources.constants.ReservationFields;
import com.qcadoo.model.api.BigDecimalUtils;
import com.qcadoo.model.api.DataDefinitionService;
import com.qcadoo.model.api.Entity;
import com.qcadoo.model.api.search.SearchRestrictions;
import com.qcadoo.tenant.api.MultiTenantCallback;
import com.qcadoo.tenant.api.MultiTenantService;
@Service
public class ReservationsService {
@Autowired
private DataDefinitionService dataDefinitionService;
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
@Autowired
private ResourceStockService resourceStockService;
@Autowired
private ResourceReservationsService resourceReservationsService;
@Autowired
private MultiTenantService multiTenantService;
private final static String L_QUANTITY = "quantity";
public void cleanReservationsTrigger() {
multiTenantService.doInMultiTenantContext(new MultiTenantCallback() {
@Override
public void invoke() {
cleanReservations();
}
});
}
public void cleanReservations() {
String sql = "DELETE FROM materialflowresources_reservation WHERE quantity = 0";
jdbcTemplate.update(sql, Maps.newHashMap());
}
public boolean reservationsEnabledForDocumentPositions() {
Entity documentPositionParameters = dataDefinitionService.get(MaterialFlowResourcesConstants.PLUGIN_IDENTIFIER,
MaterialFlowResourcesConstants.MODEL_DOCUMENT_POSITION_PARAMETERS).find().setMaxResults(1).uniqueResult();
if (documentPositionParameters != null) {
return documentPositionParameters.getBooleanField(ParameterFieldsMFR.DRAFT_MAKES_RESERVATION);
}
return false;
}
public boolean reservationsEnabledForDocumentPositions(final Entity document) {
if (document == null) {
return ReservationsService.this.reservationsEnabledForDocumentPositions();
}
String type = document.getStringField(DocumentFields.TYPE);
return ReservationsService.this.reservationsEnabledForDocumentPositions() && DocumentType.isOutbound(type) && !document.getBooleanField(DocumentFields.IN_BUFFER);
}
/**
* Creates new reservation for position with given id, using specified parameters, and updates resource stock. Uses
* jdbcTemplate.
*
* Warning! If logic in this method is changed, it should also be applied to corresponding framework method.
*
* @param params
* map containing keys: id (position id), quantity, product_id, document_id
* @see ReservationsService#createReservationFromDocumentPosition(Entity)
*/
public void createReservationFromDocumentPosition(Map<String, Object> params) {
if (!ReservationsService.this.reservationsEnabledForDocumentPositions(params)) {
return;
}
String query = "INSERT INTO materialflowresources_reservation (location_id, product_id, quantity, position_id, resource_id) "
+ "VALUES ((SELECT locationfrom_id FROM materialflowresources_document WHERE id=:document_id), :product_id, :quantity, :id, :resource_id)";
jdbcTemplate.update(query, params);
resourceStockService.updateResourceStock(params, BigDecimalUtils.convertNullToZero(params.get(L_QUANTITY)));
resourceReservationsService.updateResourceQuantites(params, BigDecimalUtils.convertNullToZero(params.get(L_QUANTITY)));
}
/**
* Creates new reservation for position and updates resource stock. Uses framework.
*
* Warning! If logic in this method is changed, it should also be applied to corresponding jdbc method.
*
* @param position
* @see ReservationsService#createReservationFromDocumentPosition(Map)
*/
public void createReservationFromDocumentPosition(final Entity position) {
if (!reservationsEnabledForDocumentPositions(position.getBelongsToField(PositionFields.DOCUMENT))) {
return;
}
Entity document = position.getBelongsToField(PositionFields.DOCUMENT);
if (DocumentState.of(document).equals(DocumentState.ACCEPTED)) {
return;
}
Entity reservation = dataDefinitionService
.get(MaterialFlowResourcesConstants.PLUGIN_IDENTIFIER, MaterialFlowResourcesConstants.MODEL_RESERVATION).create();
reservation.setField(ReservationFields.LOCATION, document.getBelongsToField(DocumentFields.LOCATION_FROM));
reservation.setField(ReservationFields.POSITION, position);
reservation.setField(ReservationFields.PRODUCT, position.getBelongsToField(PositionFields.PRODUCT));
reservation.setField(ReservationFields.QUANTITY, position.getDecimalField(PositionFields.QUANTITY));
reservation.setField(ReservationFields.RESOURCE, position.getBelongsToField(PositionFields.RESOURCE));
reservation = reservation.getDataDefinition().save(reservation);
position.setField(PositionFields.RESERVATIONS, Lists.newArrayList(reservation));
}
/**
* Updates existing reservation for position with given id, using specified parameters, and updates resource stock. Uses
* jdbcTemplate.
*
* Warning! If logic in this method is changed, it should also be applied to corresponding framework method.
*
* @param params
* map containing keys: id (position id), quantity, product_id, document_id
* @see ReservationsService#updateReservationFromDocumentPosition(Entity)
*/
public void updateReservationFromDocumentPosition(Map<String, Object> params) {
if (!ReservationsService.this.reservationsEnabledForDocumentPositions(params)) {
return;
}
if (params.get("id") != null) {
String queryForOld = "SELECT product_id, resource_id, quantity FROM materialflowresources_position WHERE id = :id";
Map<String, Object> oldPosition = jdbcTemplate.query(queryForOld, params,
new ResultSetExtractor<Map<String, Object>>() {
@Override
public Map<String, Object> extractData(ResultSet rs) throws SQLException, DataAccessException {
Map<String, Object> result = Maps.newHashMap();
if (rs.next()) {
result.put("product_id", rs.getLong("product_id"));
result.put("resource_id", rs.getLong("resource_id"));
result.put("quantity", rs.getBigDecimal("quantity"));
}
return result;
}
});
Long newResourceId = (Long) params.get("resource_id");
Long oldProductId = (Long) oldPosition.get("product_id");
Long oldResourceId = (Long) oldPosition.get("resource_id");
BigDecimal oldPositionQuantity = (BigDecimal) oldPosition.get("quantity");
BigDecimal newQuantity = BigDecimalUtils.convertNullToZero(params.get(L_QUANTITY));
BigDecimal quantityToAdd = newQuantity.subtract(oldPositionQuantity);
String query = "UPDATE materialflowresources_reservation SET "
+ "location_id = (SELECT locationfrom_id FROM materialflowresources_document WHERE id=:document_id), "
+ "product_id = :product_id, quantity = :quantity, resource_id = :resource_id WHERE position_id = :id";
jdbcTemplate.update(query, params);
if (oldProductId.compareTo((Long) params.get("product_id")) != 0) {
resourceStockService.updateResourceStock(params, newQuantity);
Map<String, Object> paramsForOld = Maps.newHashMap(params);
paramsForOld.put("product_id", oldProductId);
resourceStockService.updateResourceStock(paramsForOld, oldPositionQuantity.negate());
} else {
resourceStockService.updateResourceStock(params, quantityToAdd);
}
if (oldResourceId != null && newResourceId != null) {
if (oldResourceId.compareTo(newResourceId) != 0) {
resourceReservationsService.updateResourceQuantites(params, newQuantity);
Map<String, Object> paramsForOld = Maps.newHashMap(params);
paramsForOld.put("resource_id", oldResourceId);
resourceReservationsService.updateResourceQuantites(paramsForOld, oldPositionQuantity.negate());
} else {
resourceReservationsService.updateResourceQuantites(params, quantityToAdd);
}
} else if (oldResourceId == null && newResourceId != null) {
resourceReservationsService.updateResourceQuantites(params, newQuantity);
} else if (oldResourceId != null && newResourceId == null) {
Map<String, Object> paramsForOld = Maps.newHashMap(params);
paramsForOld.put("resource_id", oldResourceId);
resourceReservationsService.updateResourceQuantites(paramsForOld, oldPositionQuantity.negate());
}
}
}
/**
* Updates reservation for position and updates resource stock. Uses framework.
*
* Warning! If logic in this method is changed, it should also be applied to corresponding jdbc method.
*
* @param position
* @see ReservationsService#updateReservationFromDocumentPosition(Map)
*/
public void updateReservationFromDocumentPosition(final Entity position) {
Entity document = position.getBelongsToField(PositionFields.DOCUMENT);
if (!reservationsEnabledForDocumentPositions(document)){
return;
}
Entity product = position.getBelongsToField(PositionFields.PRODUCT);
Entity location = position.getBelongsToField(PositionFields.DOCUMENT).getBelongsToField(DocumentFields.LOCATION_FROM);
Entity resource = position.getBelongsToField(PositionFields.RESOURCE);
BigDecimal newQuantity = position.getDecimalField(PositionFields.QUANTITY);
Entity existingReservation = getReservationForPosition(position);
if (existingReservation != null) {
existingReservation.setField(ReservationFields.QUANTITY, newQuantity);
existingReservation.setField(ReservationFields.PRODUCT, product);
existingReservation.setField(ReservationFields.LOCATION, location);
existingReservation.setField(ReservationFields.RESOURCE, resource);
existingReservation.getDataDefinition().save(existingReservation);
}
}
/**
* Deletes reservation for position with given id and updates resource stock. Uses jdbcTemplate.
*
* Warning! If logic in this method is changed, it should also be applied to corresponding framework method.
*
* @param params
* map containing keys: id (position id), quantity, product_id, document_id
* @see ReservationsService#deleteReservationFromDocumentPosition(Entity)
*/
public void deleteReservationFromDocumentPosition(Map<String, Object> params) {
if (!reservationsEnabledForDocumentPositions(params)) {
return;
}
String query = "DELETE FROM materialflowresources_reservation WHERE position_id = :id";
jdbcTemplate.update(query, params);
resourceStockService.updateResourceStock(params, (BigDecimalUtils.convertNullToZero(params.get(L_QUANTITY))).negate());
resourceReservationsService.updateResourceQuantites(params,
BigDecimalUtils.convertNullToZero(params.get(L_QUANTITY)).negate());
}
/**
* Deletes reservation for position and updates resource stock. Uses framework.
*
* Warning! If logic in this method is changed, it should also be applied to corresponding jdbc method.
*
* @param position
* @see ReservationsService#deleteReservationFromDocumentPosition(Map)
*/
public void deleteReservationFromDocumentPosition(final Entity position) {
if (!reservationsEnabledForDocumentPositions(position.getBelongsToField(PositionFields.DOCUMENT))) {
return;
}
Entity reservation = getReservationForPosition(position);
if (reservation != null) {
reservation.getDataDefinition().delete(reservation.getId());
}
}
public Boolean reservationsEnabledForDocumentPositions(Map<String, Object> params) {
String query = "SELECT draftmakesreservation FROM materialflowresources_documentpositionparameters LIMIT 1";
Boolean enabled = jdbcTemplate.queryForObject(query, new HashMap<String, Object>() {
}, Boolean.class);
String queryForDocumentType = "SELECT type, inBuffer FROM materialflowresources_document WHERE id = :document_id";
Map<String, Object> documentMap = jdbcTemplate.queryForMap(queryForDocumentType, params);
return enabled && DocumentType.isOutbound((String)documentMap.get("type")) && !(boolean)documentMap.get("inBuffer");
}
public Entity getReservationForPosition(final Entity position) {
if (position.getId() == null) {
return null;
}
return dataDefinitionService
.get(MaterialFlowResourcesConstants.PLUGIN_IDENTIFIER, MaterialFlowResourcesConstants.MODEL_RESERVATION).find()
.add(SearchRestrictions.belongsTo(ReservationFields.POSITION, position)).setMaxResults(1).uniqueResult();
}
}