package com.qcadoo.mes.materialFlowResources;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.qcadoo.mes.basic.GridResponse;
import com.qcadoo.mes.basic.LookupUtils;
import com.qcadoo.mes.basic.controllers.dataProvider.DataProvider;
import com.qcadoo.mes.basic.controllers.dataProvider.dto.AbstractDTO;
import com.qcadoo.mes.basic.controllers.dataProvider.dto.ProductDTO;
import com.qcadoo.mes.basic.controllers.dataProvider.responses.DataResponse;
import com.qcadoo.mes.materialFlowResources.constants.DocumentState;
import com.qcadoo.mes.materialFlowResources.constants.DocumentType;
import com.qcadoo.mes.materialFlowResources.service.ReservationsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
@Repository
public class DocumentPositionService {
@Autowired
private NamedParameterJdbcTemplate jdbcTemplate;
@Autowired
private DocumentPositionValidator validator;
@Autowired
private LookupUtils lookupUtils;
@Autowired
private DataProvider dataProvider;
@Autowired
private DocumentPositionResourcesHelper positionResourcesHelper;
@Autowired
private ReservationsService reservationsService;
public GridResponse<DocumentPositionDTO> findAll(final Long documentId, final String _sidx, final String _sord, int page,
int perPage, DocumentPositionDTO position) {
String query = "SELECT %s FROM ( SELECT p.*, p.document_id as document, product.number as product, product.name as productName, product.unit, additionalcode.code as additionalcode, "
+ "palletnumber.number as palletnumber, location.number as storagelocation, resource.number as resource \n"
+ " FROM materialflowresources_position p\n"
+ " left join basic_product product on (p.product_id = product.id)\n"
+ " left join basic_additionalcode additionalcode on (p.additionalcode_id = additionalcode.id)\n"
+ " left join basic_palletnumber palletnumber on (p.palletnumber_id = palletnumber.id)\n"
+ " left join materialflowresources_resource resource on (p.resource_id = resource.id)\n"
+ " left join materialflowresources_storagelocation location on (p.storagelocation_id = location.id) WHERE p.document_id = :documentId %s) q ";
Map<String, Object> parameters = new HashMap<>();
parameters.put("documentId", documentId);
return lookupUtils.getGridResponse(query, _sidx, _sord, page, perPage, position, parameters);
}
public void delete(Long id) {
validator.validateBeforeDelete(id);
Map<String, Object> params = Maps.newHashMap();
params.put("id", id);
StringBuilder queryBuilder = new StringBuilder();
queryBuilder.append("DELETE FROM materialflowresources_position WHERE id = :id ");
String queryForDocumentId = "SELECT document_id, product_id, resource_id, quantity FROM materialflowresources_position WHERE id = :id";
Map<String, Object> result = jdbcTemplate.queryForMap(queryForDocumentId, params);
params.putAll(result);
reservationsService.deleteReservationFromDocumentPosition(params);
jdbcTemplate.update(queryBuilder.toString(), params);
}
public void create(DocumentPositionDTO documentPositionVO) {
Map<String, Object> params = validator.validateAndTryMapBeforeCreate(documentPositionVO);
if (params.get("id") == null || Long.valueOf(params.get("id").toString()) == 0) {
params.remove("id");
}
String keys = params.keySet().stream().collect(Collectors.joining(", "));
String values = params.keySet().stream().map(key -> {
return ":" + key;
}).collect(Collectors.joining(", "));
String query = String.format("INSERT INTO materialflowresources_position (%s, type, state) "
+ "VALUES (%s, (SELECT type FROM materialflowresources_document WHERE id=:document_id), (SELECT state FROM materialflowresources_document WHERE id=:document_id)) RETURNING id",
keys, values);
Long positionId = jdbcTemplate.queryForObject(query, params, Long.class);
if (positionId != null) {
params.put("id", positionId);
reservationsService.createReservationFromDocumentPosition(params);
}
}
public void update(Long id, DocumentPositionDTO documentPositionVO) {
Map<String, Object> params = validator.validateAndTryMapBeforeUpdate(documentPositionVO);
String set = params.keySet().stream().map(key -> {
return key + "=:" + key;
}).collect(Collectors.joining(", "));
String query = String.format("UPDATE materialflowresources_position "
+ "SET %s, type = (SELECT type FROM materialflowresources_document WHERE id=:document_id), state = (SELECT state FROM materialflowresources_document WHERE id=:document_id) "
+ "WHERE id = :id ", set);
reservationsService.updateReservationFromDocumentPosition(params);
jdbcTemplate.update(query, params);
}
public List<AbstractDTO> getStorageLocations(String q, String product, String document) {
if (Strings.isNullOrEmpty(q)) {
return Lists.newArrayList();
} else {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("q", '%' + q + '%');
paramMap.put("document", Integer.parseInt(document));
if (Strings.isNullOrEmpty(product)) {
String query = "SELECT id, number from materialflowresources_storagelocation WHERE number ilike :q "
+ "AND location_id IN (SELECT DISTINCT COALESCE(locationfrom_id, locationto_id) FROM materialflowresources_document where id = :document) "
+ "LIMIT 20;";
return jdbcTemplate.query(query, paramMap, new BeanPropertyRowMapper(StorageLocationDTO.class));
} else {
String query = "SELECT id, number from materialflowresources_storagelocation WHERE number ilike :q "
+ "AND location_id IN (SELECT DISTINCT COALESCE(locationfrom_id, locationto_id) FROM materialflowresources_document where id = :document) "
+ "AND (product_id IN (SELECT id FROM basic_product WHERE number LIKE :product) OR product_id IS NULL) LIMIT 20;";
paramMap.put("product", product);
return jdbcTemplate.query(query, paramMap, new BeanPropertyRowMapper(StorageLocationDTO.class));
}
}
}
public DataResponse getStorageLocationsResponse(String q, String product, String document) {
String preparedQuery;
if (Strings.isNullOrEmpty(product)) {
preparedQuery = "SELECT id, number from materialflowresources_storagelocation WHERE number ilike :query "
+ "AND location_id IN (SELECT DISTINCT COALESCE(locationfrom_id, locationto_id) FROM materialflowresources_document where id = "
+ Integer.parseInt(document) + ") AND active = true; ";
} else {
preparedQuery = "SELECT id, number from materialflowresources_storagelocation WHERE number ilike :query "
+ "AND location_id IN (SELECT DISTINCT COALESCE(locationfrom_id, locationto_id) FROM materialflowresources_document where id = "
+ Integer.parseInt(document) + ") " + "AND (product_id IN (SELECT id FROM basic_product WHERE number LIKE '"
+ product + "') OR product_id IS NULL) AND active = true;";
}
List<AbstractDTO> entities = getStorageLocations(q, product, document);
Map<String, Object> paramMap = new HashMap<>();
return dataProvider.getDataResponse(q, preparedQuery, entities, paramMap);
}
public Map<String, Object> getGridConfig(Long documentId) {
try {
String query = "select * from materialflowresources_documentpositionparametersitem order by ordering";
List<Map<String, Object>> items = jdbcTemplate.queryForList(query, Collections.EMPTY_MAP);
Map<String, Object> config = new HashMap<>();
config.put("readOnly", isGridReadOnly(documentId));
config.put("suggestResource", shouldSuggestResource());
config.put("outDocument", isOutDocument(documentId));
Map<String, Object> columns = new LinkedHashMap<>();
for (Map<String, Object> item : items) {
columns.put(item.get("name").toString(), item.get("checked"));
}
config.put("columns", columns);
return config;
} catch (EmptyResultDataAccessException e) {
return Collections.EMPTY_MAP;
}
}
public Map<String, Object> unitsOfProduct(String productNumber) {
try {
Map<String, Object> units = getUnitsFromProduct(productNumber);
units.put("available_additionalunits", getAvailableAdditionalUnitsByProduct(units));
calculateConversion(units);
return units;
} catch (EmptyResultDataAccessException e) {
return Collections.EMPTY_MAP;
}
}
public ProductDTO getProductForProductNumber(String number) {
String _query = "SELECT product.id, product.number as code, product.number, product.name, product.ean, product.globaltypeofmaterial, product.category "
+ "FROM basic_product product WHERE product.number = :number";
List<ProductDTO> products = jdbcTemplate.query(_query, Collections.singletonMap("number", number),
new BeanPropertyRowMapper(ProductDTO.class));
if (products.size() == 1) {
return products.get(0);
} else {
return null;
}
}
public void updateDocumentPositionsNumbers(final Long documentId) {
String query = "SELECT p.*, p.document_id as document, product.number as product, product.unit, additionalcode.code as additionalcode, palletnumber.number as palletnumber, "
+ "location.number as storagelocationnumber\n" + " FROM materialflowresources_position p\n"
+ " left join basic_product product on (p.product_id = product.id)\n"
+ " left join basic_additionalcode additionalcode on (p.additionalcode_id = additionalcode.id)\n"
+ " left join basic_palletnumber palletnumber on (p.palletnumber_id = palletnumber.id)\n"
+ " left join materialflowresources_storagelocation location on (p.storagelocation_id = location.id) WHERE p.document_id = :documentId ORDER BY p.number";
List<DocumentPositionDTO> list = jdbcTemplate.query(query, Collections.singletonMap("documentId", documentId),
new BeanPropertyRowMapper(DocumentPositionDTO.class));
int index = 1;
for (DocumentPositionDTO documentPositionDTO : list) {
documentPositionDTO.setNumber(index);
Map<String, Object> parameters = new HashMap<>();
parameters.put("number", documentPositionDTO.getNumber());
parameters.put("id", documentPositionDTO.getId());
String _query = "UPDATE materialflowresources_position SET number = :number WHERE id = :id ";
jdbcTemplate.update(_query, parameters);
index++;
}
}
public Long findDocumentByPosition(final Long positionId) {
String query = "SELECT p.document_id FROM materialflowresources_position p WHERE id = :id ";
Map<String, Object> parameters = new HashMap<>();
parameters.put("id", positionId);
Long documentId = jdbcTemplate.queryForObject(query, parameters, Long.class);
return documentId;
}
private List<Map<String, Object>> getAvailableAdditionalUnitsByProduct(Map<String, Object> units) {
Long productId = Long.valueOf(units.get("id").toString());
String query = "select unitto, quantityto, quantityfrom from qcadoomodel_unitconversionitem where product_id = :id";
List<Map<String, Object>> availableUnits = jdbcTemplate.queryForList(query, Collections.singletonMap("id", productId));
List<Map<String, Object>> result = availableUnits.stream().map(entry -> {
Map<String, Object> type = new HashMap<>();
type.put("value", entry.get("unitto"));
type.put("key", entry.get("unitto"));
type.put("conversion", entry.get("conversion"));
type.put("quantityto", entry.get("quantityto"));
type.put("quantityfrom", entry.get("quantityfrom"));
return type;
}).collect(Collectors.toList());
Map<String, Object> type = new HashMap<>();
type.put("value", units.get("unit"));
type.put("key", units.get("unit"));
type.put("quantityfrom", BigDecimal.valueOf(1));
type.put("quantityto", BigDecimal.valueOf(1));
result.add(type);
return result;
}
private void calculateConversion(Map<String, Object> units) {
List<Map<String, Object>> availableAdditionalUnits = (List<Map<String, Object>>) units.get("available_additionalunits");
String additionalUnit = units.get("additionalunit").toString();
Optional<Map<String, Object>> maybeEntry = availableAdditionalUnits.stream().filter(entry -> {
return entry.get("key").equals(additionalUnit);
}).findAny();
if (maybeEntry.isPresent()) {
units.put("quantityto", maybeEntry.get().get("quantityto"));
units.put("quantityfrom", maybeEntry.get().get("quantityfrom"));
} else {
units.put("quantityto", 0);
units.put("quantityfrom", 0);
}
}
private Map<String, Object> getUnitsFromProduct(String productNumber) {
String query = "SELECT id, unit, additionalunit FROM basic_product WHERE number = :number";
Map<String, Object> units = jdbcTemplate.queryForMap(query, Collections.singletonMap("number", productNumber));
if (units.get("additionalunit") == null || units.get("additionalunit").toString().isEmpty()) {
units.put("additionalunit", units.get("unit"));
}
return units;
}
private boolean isGridReadOnly(Long documentId) {
String query = "select state from materialflowresources_document WHERE id = :id";
String stateString = jdbcTemplate.queryForObject(query, Collections.singletonMap("id", documentId), String.class);
return DocumentState.parseString(stateString) == DocumentState.ACCEPTED;
}
private boolean shouldSuggestResource() {
String query = "select suggestResource from materialflowresources_documentpositionparameters limit 1";
Boolean suggestResource = jdbcTemplate.queryForObject(query, Collections.EMPTY_MAP, Boolean.class);
return suggestResource;
}
private Object isOutDocument(Long documentId) {
String query = "select type from materialflowresources_document WHERE id = :id";
String stateString = jdbcTemplate.queryForObject(query, Collections.singletonMap("id", documentId), String.class);
DocumentType type = DocumentType.parseString(stateString);
return type == DocumentType.INTERNAL_OUTBOUND || type == DocumentType.RELEASE || type == DocumentType.TRANSFER;
}
public StorageLocationDTO getStorageLocation(String product, String document) {
if (StringUtils.isEmpty(product)) {
return null;
}
String query = "select sl.id, sl.number as number, p.name as product, loc.name as location from materialflowresources_storagelocation sl join basic_product p on p.id = sl.product_id join materialflow_location loc on loc.id = sl.location_id\n"
+ "where location_id in\n"
+ "(SELECT DISTINCT COALESCE(locationfrom_id, locationto_id) as location from materialflowresources_document WHERE id = :document) AND sl.active = true AND p.number = :product LIMIT 1;";
Map<String, Object> filter = new HashMap<>();
filter.put("product", product);
filter.put("document", Integer.parseInt(document));
List<StorageLocationDTO> locations = jdbcTemplate.query(query, filter,
new BeanPropertyRowMapper(StorageLocationDTO.class));
if (locations.size() == 1) {
return locations.get(0);
} else {
return null;
}
}
public ProductDTO getProductFromLocation(String location) {
if (StringUtils.isEmpty(location)) {
return null;
}
String query = "SELECT p.number FROM materialflowresources_storagelocation l join basic_product p on l.product_id = p.id WHERE l.number = :location;";
Map<String, Object> filter = new HashMap<>();
filter.put("location", location);
List<ProductDTO> products = jdbcTemplate.query(query, filter, new BeanPropertyRowMapper(ProductDTO.class));
if (products.isEmpty()) {
return null;
} else {
return products.get(0);
}
}
public ResourceDTO getResource(Long document, String product, BigDecimal conversion, String additionalCode) {
boolean useAdditionalCode = org.apache.commons.lang3.StringUtils.isNotEmpty(additionalCode);
Map<String, Object> filter = new HashMap<>();
filter.put("product", product);
filter.put("conversion", conversion);
filter.put("context", document);
if (useAdditionalCode) {
filter.put("add_code", additionalCode);
}
String query = positionResourcesHelper.getResourceQuery(document, false,
addMethodOfDisposalCondition(document, filter, false, useAdditionalCode), useAdditionalCode);
List<ResourceDTO> batches = jdbcTemplate.query(query, filter, new BeanPropertyRowMapper(ResourceDTO.class));
if (batches.isEmpty() && useAdditionalCode) {
query = positionResourcesHelper.getResourceQuery(document, false,
addMethodOfDisposalCondition(document, filter, false, false), false);
batches = jdbcTemplate.query(query, filter, new BeanPropertyRowMapper(ResourceDTO.class));
}
batches = batches.stream().filter(resource -> resource.getAvailableQuantity().compareTo(BigDecimal.ZERO) > 0)
.collect(Collectors.toList());
if (batches.isEmpty()) {
return null;
} else {
return batches.get(0);
}
}
public List<AbstractDTO> getResources(Long document, String q, String product, BigDecimal conversion,
boolean useAdditionalCode, String additionalCode) {
if (Strings.isNullOrEmpty(q) || Strings.isNullOrEmpty(product)) {
return Lists.newArrayList();
} else {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("query", '%' + q + '%');
paramMap.put("product", product);
paramMap.put("conversion", conversion);
paramMap.put("context", document);
if (useAdditionalCode) {
paramMap.put("add_code", additionalCode);
}
String query = positionResourcesHelper.getResourceQuery(document, true,
addMethodOfDisposalCondition(document, paramMap, false, useAdditionalCode), useAdditionalCode);
return jdbcTemplate.query(query, paramMap, new BeanPropertyRowMapper(ResourceDTO.class));
}
}
public DataResponse getResourcesResponse(Long document, String q, String product, BigDecimal conversion,
String additionalCode) {
if (Strings.isNullOrEmpty(product)) {
return new DataResponse(Lists.newArrayList(), 0);
}
boolean useAdditionalCode = org.apache.commons.lang3.StringUtils.isNotEmpty(additionalCode);
String query = '%' + q + '%';
List<AbstractDTO> entities = getResources(document, query, product, conversion, useAdditionalCode, additionalCode);
if (entities.isEmpty() && useAdditionalCode) {
useAdditionalCode = false;
entities = getResources(document, query, product, conversion, false, additionalCode);
}
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("product", product);
paramMap.put("conversion", conversion);
paramMap.put("context", document);
if (useAdditionalCode) {
paramMap.put("add_code", additionalCode);
}
String preparedQuery = positionResourcesHelper.getResourceQuery(document, true,
addMethodOfDisposalCondition(document, paramMap, false, useAdditionalCode), useAdditionalCode);
return dataProvider.getDataResponse(query, preparedQuery, entities, paramMap);
}
public ResourceDTO getResourceByNumber(String resource) {
String query = "select r.*, sl.number as storageLocation, pn.number as palletNumber, ac.code as additionalCode \n"
+ "FROM materialflowresources_resource r \n"
+ "LEFT JOIN materialflowresources_storagelocation sl on sl.id = storageLocation_id \n"
+ "LEFT JOIN basic_additionalcode ac on ac.id = additionalcode_id \n"
+ "LEFT JOIN basic_palletnumber pn on pn.id = palletnumber_id WHERE r.number = :resource";
Map<String, Object> filter = new HashMap<>();
filter.put("resource", resource);
List<ResourceDTO> batches = jdbcTemplate.query(query, filter, new BeanPropertyRowMapper(ResourceDTO.class));
if (batches.isEmpty()) {
return null;
} else {
return batches.get(0);
}
}
public boolean addMethodOfDisposalCondition(Long document, Map<String, Object> paramMap, boolean useQuery,
boolean useAdditionalCode) {
// boolean addMethodOfDisposalCondition = false;
//
// String query = positionResourcesHelper.getMethodOfDisposalQuery(document, useQuery, useAdditionalCode);
// Date date = jdbcTemplate.queryForObject(query,paramMap,Date.class);
// if(date != null){
// addMethodOfDisposalCondition = true;
// }
// return addMethodOfDisposalCondition;
return true;
}
}