package com.lwo.processing.issuingservice.util.document;
import com.lwo.processing.commonapi.bean.cache.DimensionCache;
import com.lwo.processing.commonapi.dim.dto.DimItemDto;
import com.lwo.processing.issuingservice.model.CardEntity;
import com.lwo.processing.issuingservice.model.EventEntity;
import com.lwo.processing.issuingservice.model.TransactionEntity;
import com.lwo.processing.issuingservice.repository.EventRepository;
import com.lwo.processing.issuingservice.service.CommissionService;
import com.lwo.processing.issuingservice.util.cache.IssuingServiceCache;
import com.lwo.processing.springcore.config.exception.BaseRestResponseException;
import lombok.RequiredArgsConstructor;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import static org.springframework.context.i18n.LocaleContextHolder.getLocale;
@Component
@RequiredArgsConstructor
public class StatementPreparation {
private final EventRepository eventRepository;
private final CommissionService commissionService;
private final IssuingServiceCache issuingServiceCache;
private final DimensionCache<DimItemDto> dimensionCache;
private final MessageSource messageSource;
private static final String NO_CARD_TRANSACTIONS_KEY = "NoCardTransactions";
private static final String ACCOUNT_STATEMENT_DATE_FORMAT = "dd.MM.yyyy";
public Map<String, String> groupAccountTransactionsByCards(List<TransactionEntity> accountTransactions, List<TransactionEntity> financialCommissionsTransactions,
List<CardEntity> accountCards, Long accountId, ZonedDateTime startTime, ZonedDateTime endTime) {
Map<String, String> cardTransactions = new HashMap<>();
List<TransactionEntity> transactionsWithNoCard;
List<EventEntity> eventsBySpecificCard;
String stringValueOfTransactionsWithNoCard;
List<TransactionEntity> transactionsBySpecificCard;
List<TransactionEntity> financialCommissionsTransactionsBySpecificCard;
String stringValueOfTransactionsBySpecificCard;
for (CardEntity accountCard : accountCards) {
transactionsBySpecificCard = accountTransactions.stream()
.filter(transaction -> Objects.equals(transaction.getCardId(), accountCard.getCardId()))
.toList();
financialCommissionsTransactionsBySpecificCard = financialCommissionsTransactions.stream()
.filter(transaction -> Objects.equals(transaction.getCardId(), accountCard.getCardId()))
.toList();
eventsBySpecificCard = eventRepository.findAllByAccountIdAndCardIdAndRequestTimeBetweenAndUnblockingTypeIdIsNullOrderByRequestTimeDesc(accountId, accountCard.getCardId(), startTime, endTime);
stringValueOfTransactionsBySpecificCard = writeTransactionsBySpecificCardAsString(transactionsBySpecificCard, financialCommissionsTransactionsBySpecificCard,
eventsBySpecificCard, accountCard);
cardTransactions.put(accountCard.getCardNumberTruncated(), stringValueOfTransactionsBySpecificCard);
}
transactionsWithNoCard = accountTransactions.stream()
.filter(transaction -> transaction.getCardId() == null)
.toList();
stringValueOfTransactionsWithNoCard = writeTransactionsWithNoCardsString(transactionsWithNoCard);
cardTransactions.put(NO_CARD_TRANSACTIONS_KEY, stringValueOfTransactionsWithNoCard);
return cardTransactions;
}
public Map<String, String> groupTransactionsByCards(List<TransactionEntity> accountTransactions, List<TransactionEntity> financialCommissionsTransactions,
List<CardEntity> accountCards, Long accountId, ZonedDateTime startTime, ZonedDateTime endTime) {
Map<String, String> cardTransactions = new HashMap<>();
List<EventEntity> eventsBySpecificCard;
List<TransactionEntity> transactionsBySpecificCard;
List<TransactionEntity> financialCommissionsTransactionsBySpecificCard;
String stringValueOfTransactionsBySpecificCard;
for (CardEntity accountCard : accountCards) {
transactionsBySpecificCard = accountTransactions.stream()
.filter(transaction -> Objects.equals(transaction.getCardId(), accountCard.getCardId()))
.toList();
financialCommissionsTransactionsBySpecificCard = financialCommissionsTransactions.stream()
.filter(transaction -> Objects.equals(transaction.getCardId(), accountCard.getCardId()))
.toList();
eventsBySpecificCard = eventRepository.findAllByAccountIdAndCardIdAndRequestTimeBetweenAndUnblockingTypeIdIsNullOrderByRequestTimeDesc(accountId, accountCard.getCardId(), startTime, endTime);
stringValueOfTransactionsBySpecificCard = writeTransactionsBySpecificCardAsString(transactionsBySpecificCard, financialCommissionsTransactionsBySpecificCard,
eventsBySpecificCard, accountCard);
cardTransactions.put(accountCard.getCardNumberTruncated(), stringValueOfTransactionsBySpecificCard);
}
System.out.println(cardTransactions);
return cardTransactions;
}
public void clearCardTransaction(Map<String, String> cardTransactions) {
List<String> emptyCardNumbers = new ArrayList<>();
for (Map.Entry<String, String> card : cardTransactions.entrySet()) {
if (card.getKey().equals("NoCardTransactions")) {
continue;
}
String[] cardHolderNameAndTransactions = card.getValue().split("\n");
if (cardHolderNameAndTransactions.length == 1) {
emptyCardNumbers.add(card.getKey());
}
}
for (String emptyCardNumber : emptyCardNumbers) {
cardTransactions.remove(emptyCardNumber);
}
}
public void checkPeriod(LocalDate startDate, LocalDate endDate) {
if (startDate.isAfter(endDate)) {
throw new BaseRestResponseException("ISSUING_37", "ISSUING_37", "ISSUING_37", messageSource.getMessage("ISSUING_37", null, getLocale()));
}
if (startDate.plusDays(90).isBefore(endDate)) {
throw new BaseRestResponseException("ISSUING_38", "ISSUING_38", "ISSUING_38", messageSource.getMessage("ISSUING_38", null, getLocale()));
}
}
private String writeTransactionsWithNoCardsString(List<TransactionEntity> transactionsWithNoCard) {
StringBuilder stringValueOfTransactionsWithNoCard = new StringBuilder();
String amount;
String description;
for (TransactionEntity transaction : transactionsWithNoCard) {
amount = processAmountForCustomerReadability(transaction.getAmount());
description = Objects.nonNull(transaction.getDescription()) ? transaction.getDescription() : "";
stringValueOfTransactionsWithNoCard
.append(transaction.getBusinessDate().format(DateTimeFormatter.ofPattern(ACCOUNT_STATEMENT_DATE_FORMAT)))
.append("|")
.append(amount)
.append("|")
.append(description)
.append("\n");
}
return stringValueOfTransactionsWithNoCard.toString();
}
private String writeTransactionsBySpecificCardAsString(List<TransactionEntity> transactionsBySpecificCard, List<TransactionEntity> commissionsTransactionsBySpecificCard,
List<EventEntity> eventsBySpecificCard, CardEntity specificCard) {
StringBuilder stringValueOfTransactionsBySpecificCard = new StringBuilder();
stringValueOfTransactionsBySpecificCard.append(specificCard.getPrintedName()).append("\n");
/*
String amount;
String notFormattedDateTime;
String formattedDateTime;
String description;
for (TransactionEntity transaction : transactionsBySpecificCard) {
amount = processAmountForCustomerReadability(transaction.getAmount());
if (Objects.nonNull(transaction.getTransactionDate())) {
if (Objects.nonNull(transaction.getTransactionDate().get("Date")) && Objects.nonNull(transaction.getTransactionDate().get("Time"))) {
notFormattedDateTime = String.valueOf(transaction.getTransactionDate().get("Date")).concat(String.valueOf(transaction.getTransactionDate().get("Time")));
formattedDateTime = formatDateAndTime(notFormattedDateTime);
} else {
formattedDateTime = "";
}
} else {
formattedDateTime = "";
}
description = Objects.nonNull(transaction.getDescription()) ? transaction.getDescription() : "";
stringValueOfTransactionsBySpecificCard
.append(transaction.getTransactionTime().format(DateTimeFormatter.ofPattern(ACCOUNT_STATEMENT_DATE_FORMAT)))
.append("|")
.append(amount)
.append("|")
.append(formattedDateTime)
.append("|")
.append(description)
.append("\n");
}
*/
String iterator;
String procDate;
String status;
String transactionDateTime;
String description;
String mcc;
String place;
String rrn;
String authCode;
String currency;
String currencyAmount;
String commission;
String amount;
List<Long> commissionsTransactionTypes = commissionService.getCommissionTransactionTypes()
.stream()
.map(DimItemDto::getDimId)
.collect(Collectors.toList());
List<ZonedDateTime> transactionDates = transactionsBySpecificCard.stream().map(TransactionEntity::getTransactionTime).toList();
List<ZonedDateTime> eventDates = eventsBySpecificCard.stream().map(EventEntity::getRequestTime).toList();
List<ZonedDateTime> allDates = new ArrayList<>();
eventDates = eventDates.stream().sorted(Comparator.reverseOrder()).distinct().collect(Collectors.toList());
transactionDates = transactionDates.stream().sorted(Comparator.reverseOrder()).distinct().collect(Collectors.toList());
allDates.addAll(eventDates);
allDates.addAll(transactionDates);
// allDates = allDates.stream().sorted(Comparator.reverseOrder()).distinct().collect(Collectors.toList());
//DimItemDto nonFinFeeDimDimension = dimensionCache.dimCodeToItem("TRANSACTION_TYPE", "NONFIN_FEE");
int iteratorValue = 0;
for (ZonedDateTime date : allDates) {
List<TransactionEntity> transactionsByDate = transactionsBySpecificCard.stream().filter(transaction -> transaction.getTransactionTime().isEqual(date)).toList();
List<EventEntity> eventsByDate = eventsBySpecificCard.stream().filter(event -> event.getRequestTime().isEqual(date)).toList();
for (TransactionEntity transaction : transactionsByDate) {
List<TransactionEntity> commissionsByTransaction = commissionsTransactionsBySpecificCard.stream()
.filter(commissionTransaction -> transaction.getTransactionId().equals(commissionTransaction.getMainTransactionId()))
.collect(Collectors.toList());
iteratorValue++;
stringValueOfTransactionsBySpecificCard.append(generateStatementByTransaction(iteratorValue, transaction, commissionsTransactionTypes));
if (!commissionsByTransaction.isEmpty())
for (TransactionEntity commissionTransaction : commissionsByTransaction) {
stringValueOfTransactionsBySpecificCard.append(generateStatementByTransaction(null, commissionTransaction, commissionsTransactionTypes));
}
}
for (EventEntity event : eventsByDate) {
iteratorValue++;
iterator = String.valueOf(iteratorValue);
procDate = "";
status = "ЗАБЛОКИРОВАНА";
/*nonFormattedTransactionDateTime = String.valueOf(event.getRequestTime());
transactionDateTime = formatDateAndTime(nonFormattedTransactionDateTime);*/
transactionDateTime = event.getRequestTime().format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss"));
description = "";
if (Objects.nonNull(event.getMsgType()) && Objects.nonNull(event.getFld003())) {
Integer isReversal;
if (event.getMsgType().startsWith("14")) {
isReversal = 1;
} else {
isReversal = 0;
}
DimItemDto eventTransactionType = issuingServiceCache.getTransactionTypeByProcCodeAndReversalAttr(
event.getFld003().substring(0, 2),
isReversal
);
if (Objects.nonNull(eventTransactionType)) {
description = eventTransactionType.getName();
}
}
BigDecimal eventAmountSign = getAmountSign(event);
if (Objects.nonNull(event.getEventData())) {
if (Objects.nonNull(event.getEventData().get("fld026"))) {
mcc = (String) event.getEventData().get("fld026");
} else {
mcc = "";
}
} else {
mcc = "";
}
if (Objects.nonNull(event.getEventData())) {
if (Objects.nonNull(event.getEventData().get("fld043"))) {
place = (String) event.getEventData().get("fld043");
} else {
place = "";
}
} else {
place = "";
}
if (Objects.nonNull(event.getEventData())) {
if (Objects.nonNull(event.getEventData().get("fld037"))) {
rrn = (String) event.getEventData().get("fld037");
} else {
rrn = "";
}
} else {
rrn = "";
}
if (Objects.nonNull(event.getEventData())) {
if (Objects.nonNull(event.getEventData().get("fld038"))) {
authCode = (String) event.getEventData().get("fld038");
} else {
authCode = "";
}
} else {
authCode = "";
}
DimItemDto eventTransactionCurrency = null;
if (Objects.nonNull(event.getEventData())) {
if (Objects.nonNull(event.getEventData().get("fld049"))) {
eventTransactionCurrency = issuingServiceCache.getCurrencyByNumericCode(event.getEventData().get("fld049").toString());
currency = eventTransactionCurrency.getCode();
} else {
currency = "";
}
} else {
currency = "";
}
if (Objects.nonNull(event.getEventData())) {
if (Objects.nonNull(event.getEventData().get("fld004"))) {
Integer exponent = eventTransactionCurrency.getAttributes().get("EXPONENT").getIntValue();
BigDecimal eventAmountWithExponent = new BigDecimal(event.getEventData().get("fld004").toString())
.movePointLeft(exponent)
.setScale(exponent, RoundingMode.HALF_UP);
eventAmountWithExponent = eventAmountWithExponent.multiply(eventAmountSign);
currencyAmount = processAmountForCustomerReadability(eventAmountWithExponent);
} else {
currencyAmount = "";
}
} else {
currencyAmount = "";
}
commission = "";
if (Objects.nonNull(event.getEventData())) {
if (Objects.nonNull(event.getEventData().get("fld006"))) {
DimItemDto eventCurrency = issuingServiceCache.getCurrencyByNumericCode(event.getEventData().get("fld051").toString());
Integer exponent = eventCurrency.getAttributes().get("EXPONENT").getIntValue();
BigDecimal eventAmount = new BigDecimal(event.getEventData().get("fld006").toString())
.movePointLeft(exponent)
.setScale(exponent, RoundingMode.HALF_UP);
eventAmount = eventAmount.multiply(eventAmountSign);
amount = processAmountForCustomerReadability(eventAmount);
} else {
amount = "";
}
} else {
amount = "";
}
stringValueOfTransactionsBySpecificCard
.append(iterator)
.append("|")
.append(procDate)
.append("|")
.append(status)
.append("|")
.append(transactionDateTime)
.append("|")
.append(description)
.append("|")
.append(mcc)
.append("|")
.append(place)
.append("|")
.append(rrn)
.append("|")
.append(authCode)
.append("|")
.append(currency)
.append("|")
.append(currencyAmount)
.append("|")
.append(commission)
.append("|")
.append(amount)
.append("\n");
}
}
return stringValueOfTransactionsBySpecificCard.toString();
}
private String generateStatementByTransaction(Integer iteratorValue, TransactionEntity transaction, List<Long> commissionTransactionsTypes) {
String status;
String nonFormattedTransactionDateTime;
String rrn;
String transactionDateTime;
String commission;
String place;
String description;
String amount;
String procDate;
String mcc;
String currency;
String iterator;
String authCode;
String currencyAmount;
iterator = iteratorValue != null ? String.valueOf(iteratorValue) : "";
if (Objects.nonNull(transaction.getTransactionDate())) {
//if (Objects.nonNull(transaction.getTransactionDate().get("Date"))) {
// nonFormattedProcDate = String.valueOf(transaction.getTransactionDate().get("Date"));
procDate = transaction.getBusinessDate().format(DateTimeFormatter.ofPattern("dd.MM.yyyy"));
//}
} else {
procDate = "";
}
status = "ПРОВЕДЕНА";
if (Objects.nonNull(transaction.getTransactionDate())) {
if (Objects.nonNull(transaction.getTransactionDate().get("Date")) && Objects.nonNull(transaction.getTransactionDate().get("Time"))) {
nonFormattedTransactionDateTime = String.valueOf(transaction.getTransactionDate().get("Date")).concat(String.valueOf(transaction.getTransactionDate().get("Time")));
transactionDateTime = formatDateAndTime(nonFormattedTransactionDateTime);
} else {
transactionDateTime = "";
}
} else {
transactionDateTime = "";
}
if (!commissionTransactionsTypes.contains(transaction.getTransactionTypeId())) {
description = Objects.nonNull(transaction.getDescription()) ? transaction.getDescription() : "";
} else {
description = dimensionCache.dimIdToItem(Long.valueOf((Integer) transaction.getTransactionDate().get("FEE_commission_LCIS_code_id"))).getName();
}
if (Objects.nonNull(transaction.getTransactionDate())) {
if (Objects.nonNull(transaction.getTransactionDate().get("MCC_code"))) {
mcc = (String) transaction.getTransactionDate().get("MCC_code");
} else {
mcc = "";
}
} else {
mcc = "";
}
if (Objects.nonNull(transaction.getTransactionDate())) {
if (Objects.nonNull(transaction.getTransactionDate().get("Abvr_name"))) {
place = (String) transaction.getTransactionDate().get("Abvr_name");
} else {
place = "";
}
} else {
place = "";
}
if (Objects.nonNull(transaction.getTransactionDate())) {
if (Objects.nonNull(transaction.getTransactionDate().get("Ref_number"))) {
rrn = (String) transaction.getTransactionDate().get("Ref_number");
} else {
rrn = "";
}
} else {
rrn = "";
}
if (Objects.nonNull(transaction.getTransactionDate())) {
if (Objects.nonNull(transaction.getTransactionDate().get("Appr_code"))) {
authCode = (String) transaction.getTransactionDate().get("Appr_code");
} else {
authCode = "";
}
} else {
authCode = "";
}
if (Objects.nonNull(transaction.getTransactionDate())) {
if (Objects.nonNull(transaction.getTransactionDate().get("Currency"))) {
currency = (String) transaction.getTransactionDate().get("Currency");
} else {
currency = "";
}
} else {
currency = "";
}
if (Objects.nonNull(transaction.getTransactionDate())) {
if (Objects.nonNull(transaction.getTransactionDate().get("Amount")) && Objects.nonNull(transaction.getTransactionDate().get("Currency"))) {
currencyAmount = processCurrencyAmount(transaction.getTransactionDate().get("Amount").toString(), dimensionCache.dimCodeToItem("CURRENCY", currency).getAttributes().get("EXPONENT").getIntValue());
currencyAmount = processAmountForCustomerReadability(currencyAmount, dimensionCache.dimIdToItem(transaction.getTransactionTypeId()));
} else {
currencyAmount = "";
}
} else {
currencyAmount = "";
}
commission = "";
amount = "";
if (commissionTransactionsTypes.contains(transaction.getTransactionTypeId())) {
commission = processAmountForCustomerReadability(transaction.getAmount());
} else {
amount = processAmountForCustomerReadability(transaction.getAmount());
}
return iterator +
"|" + procDate +
"|" + status +
"|" + transactionDateTime +
"|" + description +
"|" + mcc +
"|" + place +
"|" + rrn +
"|" + authCode +
"|" + currency +
"|" + currencyAmount +
"|" + commission +
"|" + amount +
"|" + "end" + "\n";
}
private String processCurrencyAmount(String amount, Integer exponent) {
BigDecimal bdAmount = new BigDecimal(amount);
bdAmount = bdAmount.setScale(exponent, RoundingMode.HALF_UP);
return bdAmount.toString();
}
private String processAmountForCustomerReadability(String amount, DimItemDto transactionType) {
if (transactionType.getAttributes().containsKey("DEBIT_CREDIT")){
if ("C".equals(transactionType.getAttributes().get("DEBIT_CREDIT").getStringValue())) {
return amount + "+";
} else if ("D".equals(transactionType.getAttributes().get("DEBIT_CREDIT").getStringValue())) {
return amount + "-";
} else {
return "";
}
} else {
return "";
}
}
private String formatDateAndTime(String notFormattedDateTime) {
DateTimeFormatter toStringParser = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm:ss");
DateTimeFormatter toDateParser = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
ZonedDateTime dateValue;
try {
dateValue = LocalDateTime.parse(notFormattedDateTime, toDateParser).atZone(ZoneId.systemDefault());
} catch (Exception e) {
dateValue = ZonedDateTime.of(LocalDate.parse(notFormattedDateTime, toDateParser), LocalTime.MIN, ZoneId.systemDefault());
}
return dateValue.format(toStringParser);
}
private BigDecimal getAmountSign(EventEntity event) {
if (event.getMsgType().startsWith("11")) {
if (event.getFld003().startsWith("00") || event.getFld003().startsWith("01") || event.getFld003().startsWith("10") || event.getFld003().startsWith("11")) {
return BigDecimal.ONE.negate();
} else if (event.getFld003().startsWith("21") || event.getFld003().startsWith("26")) {
return BigDecimal.ONE;
} else {
return BigDecimal.ONE;
}
} else if (event.getMsgType().startsWith("14")) {
if (event.getFld003().startsWith("21") || event.getFld003().startsWith("26")) {
return BigDecimal.ONE.negate();
} else if (event.getFld003().startsWith("00") || event.getFld003().startsWith("01") || event.getFld003().startsWith("10") || event.getFld003().startsWith("11")) {
return BigDecimal.ONE;
} else {
return BigDecimal.ONE;
}
} else {
return BigDecimal.ONE;
}
}
private String processAmountForCustomerReadability(BigDecimal amount) {
if (amount.toString().startsWith("-")) {
return amount.toString().replaceAll("-", "").concat("-");
}
return amount.toString().concat("+");
}
}