blob: 245f3adbdb827fd7f04416f2027e552bc5e4aa93 [file] [log] [blame]
/*
* Copyright 2017 The Mifos Initiative.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.mifos.reporting.service.internal.specification;
import io.mifos.core.api.util.UserContextHolder;
import io.mifos.core.lang.DateConverter;
import io.mifos.reporting.api.v1.domain.*;
import io.mifos.reporting.service.ServiceConstants;
import io.mifos.reporting.service.spi.*;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.time.Clock;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;
@Report(category = "Teller", identifier = "Transactions")
public class TellerTransactionReportSpecification implements ReportSpecification {
private static final String TELLER_ID = "Teller Id";
private static final String TELLER = "Teller";
private static final String TRANSACTION_TYPE = "Transaction Type";
private static final String TRANSACTION_DATE = "Transaction Date";
private static final String CUSTOMER = "Customer";
private static final String SOURCE = "Source Account";
private static final String TARGET = "Target Account";
private static final String CLERK = "Clerk";
private static final String AMOUNT = "Amount";
private static final String STATUS = "Status";
private final Logger logger;
private final EntityManager entityManager;
private final HashMap<String, String> tellerColumnMapping = new HashMap<>();
private final HashMap<String, String> transactionColumnMapping = new HashMap<>();
private final HashMap<String, String> allColumnMapping = new HashMap<>();
@Autowired
public TellerTransactionReportSpecification(@Qualifier(ServiceConstants.LOGGER_NAME) final Logger logger,
final EntityManager entityManager) {
super();
this.logger = logger;
this.entityManager = entityManager;
this.initializeMapping();
}
@Override
public ReportDefinition getReportDefinition() {
final ReportDefinition reportDefinition = new ReportDefinition();
reportDefinition.setIdentifier("Transactions");
reportDefinition.setName("Teller Transactions");
reportDefinition.setDescription("List all teller-cashier transactions.");
reportDefinition.setQueryParameters(this.buildQueryParameters());
reportDefinition.setDisplayableFields(this.buildDisplayableFields());
return reportDefinition;
}
@Override
public ReportPage generateReport(ReportRequest reportRequest, int pageIndex, int size) {
final ReportDefinition reportDefinition = this.getReportDefinition();
this.logger.info("Generating report {0}.", reportDefinition.getIdentifier());
final ReportPage reportPage = new ReportPage();
reportPage.setName(reportDefinition.getName());
reportPage.setDescription(reportDefinition.getDescription());
reportPage.setHeader(this.createHeader(reportRequest.getDisplayableFields()));
final Query tellerQuery = this.entityManager.createNativeQuery(this.buildTellerQuery(reportRequest, pageIndex, size));
final List<?> tellerResultList = tellerQuery.getResultList();
reportPage.setRows(this.buildRows(reportRequest, tellerResultList));
reportPage.setHasMore(
!this.entityManager.createNativeQuery(this.buildTellerQuery(reportRequest, pageIndex + 1, size))
.getResultList().isEmpty()
);
reportPage.setGeneratedBy(UserContextHolder.checkedGetUser());
reportPage.setGeneratedOn(DateConverter.toIsoString(LocalDateTime.now(Clock.systemUTC())));
return reportPage;
}
@Override
public void validate(ReportRequest reportRequest) throws IllegalArgumentException {
final ArrayList<String> unknownFields = new ArrayList<>();
reportRequest.getQueryParameters().forEach(queryParameter -> {
if (!this.allColumnMapping.keySet().contains(queryParameter.getName())) {
unknownFields.add(queryParameter.getName());
}
});
reportRequest.getDisplayableFields().forEach(displayableField -> {
if (!this.allColumnMapping.keySet().contains(displayableField.getName())) {
unknownFields.add(displayableField.getName());
}
});
if (!unknownFields.isEmpty()) {
throw new IllegalArgumentException(
"Unspecified fields requested: " + unknownFields.stream().collect(Collectors.joining(", "))
);
}
}
private void initializeMapping() {
this.tellerColumnMapping.put(TELLER_ID, "teller.id");
this.tellerColumnMapping.put(TELLER, "teller.identifier");
this.transactionColumnMapping.put(TRANSACTION_TYPE, "trx.transaction_type");
this.transactionColumnMapping.put(TRANSACTION_DATE, "trx.transaction_date");
this.transactionColumnMapping.put(CUSTOMER, "trx.customer_identifier");
this.transactionColumnMapping.put(SOURCE, "trx.customer_account_identifier");
this.transactionColumnMapping.put(TARGET, "trx.target_account_identifier");
this.transactionColumnMapping.put(CLERK, "trx.clerk");
this.transactionColumnMapping.put(AMOUNT, "trx.amount");
this.transactionColumnMapping.put(STATUS, "trx.a_state");
this.allColumnMapping.putAll(tellerColumnMapping);
this.allColumnMapping.putAll(transactionColumnMapping);
}
private Header createHeader(final List<DisplayableField> displayableFields) {
final Header header = new Header();
header.setColumnNames(
displayableFields
.stream()
.map(DisplayableField::getName)
.collect(Collectors.toList())
);
return header;
}
private List<Row> buildRows(final ReportRequest reportRequest, final List<?> tellerResultList) {
final ArrayList<Row> rows = new ArrayList<>();
tellerResultList.forEach(result -> {
final Row row = new Row();
row.setValues(new ArrayList<>());
final String tellerIdentifier;
if (result instanceof Object[]) {
final Object[] resultValues = (Object[]) result;
tellerIdentifier = resultValues[0].toString();
for (final Object resultValue : resultValues) {
final Value value = new Value();
if (resultValue != null) {
value.setValues(new String[]{resultValue.toString()});
} else {
value.setValues(new String[]{});
}
row.getValues().add(value);
}
} else {
tellerIdentifier = result.toString();
final Value value = new Value();
value.setValues(new String[]{result.toString()});
row.getValues().add(value);
}
final String transactionQueryString = this.buildTellerTransactionQuery(reportRequest, tellerIdentifier);
final Query transactionQuery = this.entityManager.createNativeQuery(transactionQueryString);
final List<?> resultList = transactionQuery.getResultList();
final ArrayList<String> transactionType = new ArrayList<>();
final ArrayList<String> transactionDate = new ArrayList<>();
final ArrayList<String> customer = new ArrayList<>();
final ArrayList<String> source = new ArrayList<>();
final ArrayList<String> target = new ArrayList<>();
final ArrayList<String> clerk = new ArrayList<>();
final ArrayList<String> amount = new ArrayList<>();
final ArrayList<String> status = new ArrayList<>();
resultList.forEach(transaction -> {
final Object[] transactionValue = (Object[]) transaction;
for (int i = 0; i < transactionValue.length; i++) {
if (i == 0 && transactionValue[0] != null) {
transactionType.add(transactionValue[0].toString());
}
if (i == 1 && transactionValue[1] != null) {
transactionDate.add(transactionValue[1].toString());
}
if (i == 2 && transactionValue[2] != null) {
customer.add(transactionValue[2].toString());
}
if (i == 3 && transactionValue[3] != null) {
source.add(transactionValue[3].toString());
}
if (i == 4 && transactionValue[4] != null) {
target.add(transactionValue[4].toString());
}
if (i == 5 && transactionValue[5] != null) {
clerk.add(transactionValue[5].toString());
}
if (i == 6 && transactionValue[6] != null) {
amount.add(transactionValue[6].toString());
}
if (i == 7 && transactionValue[7] != null) {
status.add(transactionValue[7].toString());
}
}
}
);
final Value transactionTypeValue = new Value();
transactionTypeValue.setValues(transactionType.toArray(new String[transactionType.size()]));
row.getValues().add(transactionTypeValue);
final Value transactionDateValue = new Value();
transactionDateValue.setValues(transactionDate.toArray(new String[transactionDate.size()]));
row.getValues().add(transactionDateValue);
final Value customerValue = new Value();
customerValue.setValues(customer.toArray(new String[customer.size()]));
row.getValues().add(customerValue);
final Value sourceValue = new Value();
sourceValue.setValues(source.toArray(new String[source.size()]));
row.getValues().add(sourceValue);
final Value targetValue = new Value();
targetValue.setValues(target.toArray(new String[target.size()]));
row.getValues().add(targetValue);
final Value clerkValue = new Value();
clerkValue.setValues(clerk.toArray(new String[clerk.size()]));
row.getValues().add(clerkValue);
final Value amountValue = new Value();
amountValue.setValues(amount.toArray(new String[amount.size()]));
row.getValues().add(amountValue);
final Value statusValue = new Value();
statusValue.setValues(status.toArray(new String[status.size()]));
row.getValues().add(statusValue);
rows.add(row);
});
return rows;
}
private List<DisplayableField> buildDisplayableFields() {
return Arrays.asList(
DisplayableFieldBuilder.create(TELLER_ID, Type.TEXT).mandatory().build(),
DisplayableFieldBuilder.create(TELLER, Type.TEXT).mandatory().build(),
DisplayableFieldBuilder.create(TRANSACTION_TYPE, Type.TEXT).mandatory().build(),
DisplayableFieldBuilder.create(TRANSACTION_DATE, Type.DATE).mandatory().build(),
DisplayableFieldBuilder.create(CUSTOMER, Type.TEXT).mandatory().build(),
DisplayableFieldBuilder.create(SOURCE, Type.TEXT).mandatory().build(),
DisplayableFieldBuilder.create(TARGET, Type.TEXT).mandatory().build(),
DisplayableFieldBuilder.create(CLERK, Type.TEXT).mandatory().build(),
DisplayableFieldBuilder.create(AMOUNT, Type.TEXT).mandatory().build(),
DisplayableFieldBuilder.create(STATUS, Type.TEXT).mandatory().build()
);
}
private List<QueryParameter> buildQueryParameters() {
return Arrays.asList(
QueryParameterBuilder.create(TRANSACTION_DATE, Type.DATE).operator(QueryParameter.Operator.BETWEEN).build(),
QueryParameterBuilder.create(STATUS, Type.TEXT).operator(QueryParameter.Operator.IN).build()
);
}
private String buildTellerQuery(ReportRequest reportRequest, int pageIndex, int size) {
final StringBuilder query = new StringBuilder("SELECT ");
final List<DisplayableField> displayableFields = reportRequest.getDisplayableFields();
final ArrayList<String> columns = new ArrayList<>();
displayableFields.forEach(displayableField -> {
final String column = this.tellerColumnMapping.get(displayableField.getName());
if (column != null) {
columns.add(column);
}
});
query.append(columns.stream().collect(Collectors.joining(", ")))
.append(" FROM ")
.append("tajet_teller teller ");
query.append(" ORDER BY teller.id");
query.append(" LIMIT ");
query.append(size);
if (pageIndex > 0) {
query.append(" OFFSET ");
query.append(size * pageIndex);
}
return query.toString();
}
private String buildTellerTransactionQuery(final ReportRequest reportRequest, final String tellerIdentifier) {
final StringBuilder query = new StringBuilder("SELECT ");
final List<DisplayableField> displayableFields = reportRequest.getDisplayableFields();
final ArrayList<String> columns = new ArrayList<>();
displayableFields.forEach(displayableField -> {
final String column = this.transactionColumnMapping.get(displayableField.getName());
if (column != null) {
columns.add(column);
}
});
query.append(columns.stream().collect(Collectors.joining(", ")))
.append(" FROM ")
.append("tajet_teller_transactions trx " +
"LEFT JOIN tajet_teller teller on trx.teller_id = teller.id ");
query.append("WHERE teller.id ='" + tellerIdentifier + "'");
final List<QueryParameter> queryParameters = reportRequest.getQueryParameters();
if (!queryParameters.isEmpty()) {
final ArrayList<String> criteria = new ArrayList<>();
queryParameters.forEach(queryParameter -> {
if (queryParameter.getValue() != null && !queryParameter.getValue().isEmpty()) {
criteria.add(
CriteriaBuilder.buildCriteria(this.transactionColumnMapping.get(queryParameter.getName()), queryParameter)
);
}
});
if (!criteria.isEmpty()) {
query.append(" AND ");
query.append(criteria.stream().collect(Collectors.joining(" AND ")));
}
}
return query.toString();
}
}