diff --git a/src/main/java/com/mfsys/uco/service/TransactionService.java b/src/main/java/com/mfsys/uco/service/TransactionService.java index 27acbc5..59abba1 100644 --- a/src/main/java/com/mfsys/uco/service/TransactionService.java +++ b/src/main/java/com/mfsys/uco/service/TransactionService.java @@ -24,6 +24,7 @@ import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDate; import java.time.temporal.ChronoUnit; +import java.util.Currency; import java.util.*; @Service @@ -183,12 +184,19 @@ public class TransactionService { } public EvaluatedCurrencyReponse getEvaluatedCurrency(String porOrgacode, String baseCurrencyCode, String targetCurrencyCode, double sgtGntramtfc) { - double pkrAmt = convertToPKR(baseCurrencyCode,sgtGntramtfc,porOrgacode); - double convertFromPkr = convertFromPKR(targetCurrencyCode,pkrAmt,porOrgacode); - List exchangeRateModelList = fetchExchangeRate(porOrgacode); + List exchangeRateModelList = fetchExchangeRate(porOrgacode); + if (exchangeRateModelList == null || exchangeRateModelList.isEmpty()) { + throw new RuntimeException("No exchange rates configured for organization " + porOrgacode + ". Please configure exchange rates first."); + } + + String normalizedBase = normalizeCurrencyCode(baseCurrencyCode, exchangeRateModelList); + String normalizedTarget = normalizeCurrencyCode(targetCurrencyCode, exchangeRateModelList); + + double pkrAmt = convertToPKR(normalizedBase, sgtGntramtfc, porOrgacode, exchangeRateModelList); + double convertFromPkr = convertFromPKR(normalizedTarget, pkrAmt, porOrgacode, exchangeRateModelList); Optional rate = exchangeRateModelList.stream() - .filter(x -> x.isPcrCurrbase() && x.getPcrCurrcode().equals(targetCurrencyCode)) + .filter(x -> x.isPcrCurrbase() && currencyCodeMatches(x.getPcrCurrcode(), normalizedTarget)) .map(x -> { BigDecimal bd = BigDecimal.valueOf(1 / x.getPerEratrateact()); bd = bd.setScale(2, RoundingMode.HALF_UP); @@ -198,7 +206,7 @@ public class TransactionService { if (!rate.isPresent()) { rate = exchangeRateModelList.stream() - .filter(x -> !x.isPcrCurrbase() && x.getPcrCurrcode().equals(targetCurrencyCode)) + .filter(x -> !x.isPcrCurrbase() && currencyCodeMatches(x.getPcrCurrcode(), normalizedTarget)) .map(x -> { BigDecimal bd = BigDecimal.valueOf(x.getPerEratrateact()); bd = bd.setScale(2, RoundingMode.HALF_UP); @@ -220,22 +228,30 @@ public class TransactionService { public double convertToPKR(String fromcrCurrcode, double amount, String porOrgacode) { - List exchangeRateModelList = fetchExchangeRate(porOrgacode); + return convertToPKR(fromcrCurrcode, amount, porOrgacode, exchangeRateModelList); + } + + private double convertToPKR(String fromcrCurrcode, double amount, String porOrgacode, List exchangeRateModelList) { + if (exchangeRateModelList == null || exchangeRateModelList.isEmpty()) { + throw new RuntimeException("No exchange rates configured for organization " + porOrgacode + ". Please configure exchange rates first."); + } String baseCurrencyCode = exchangeRateModelList.stream() .filter(ExchangeRateModel::isPcrCurrbase) .findFirst() .map(ExchangeRateModel::getPcrCurrcode) .orElse(null); - if (fromcrCurrcode.equals(baseCurrencyCode)) { + if (currencyCodeMatches(baseCurrencyCode, fromcrCurrcode)) { return amount; } return exchangeRateModelList.stream() - .filter(k -> k.getPcrCurrcode().equals(fromcrCurrcode)) + .filter(k -> currencyCodeMatches(k.getPcrCurrcode(), fromcrCurrcode)) .findFirst() .map(k -> amount * k.getPerEratrateact()) - .orElse(0.0); + .orElseThrow(() -> new RuntimeException( + "Exchange rate not found for currency " + fromcrCurrcode + " (organization: " + porOrgacode + "). Available: " + formatAvailableCurrencies(exchangeRateModelList) + )); } public List fetchExchangeRate(String porOrgacode) { ObjectMapper objectMapper = new ObjectMapper(); @@ -243,23 +259,105 @@ public class TransactionService { new TypeReference>() { }); } - public double convertFromPKR(String todrCurrcode, double amount,String porOrgacode) { + public double convertFromPKR(String todrCurrcode, double amount, String porOrgacode) { List exchangeRateModelList = fetchExchangeRate(porOrgacode); + return convertFromPKR(todrCurrcode, amount, porOrgacode, exchangeRateModelList); + } + private double convertFromPKR(String todrCurrcode, double amount, String porOrgacode, List exchangeRateModelList) { + if (exchangeRateModelList == null || exchangeRateModelList.isEmpty()) { + throw new RuntimeException("No exchange rates configured for organization " + porOrgacode + ". Please configure exchange rates first."); + } String baseCurrencyCode = exchangeRateModelList.stream() .filter(ExchangeRateModel::isPcrCurrbase) .findFirst() .map(ExchangeRateModel::getPcrCurrcode) .orElse(null); - if (todrCurrcode.equals(baseCurrencyCode)) { + if (currencyCodeMatches(baseCurrencyCode, todrCurrcode)) { return amount; } - return exchangeRateModelList.stream() - .filter(k -> k.getPcrCurrcode().equals(todrCurrcode)) + .filter(k -> currencyCodeMatches(k.getPcrCurrcode(), todrCurrcode)) .findFirst() .map(k -> amount / k.getPerEratrateact()) - .orElseThrow(() -> new RuntimeException("Product Not Found")); + .orElseThrow(() -> new RuntimeException( + "Exchange rate not found for currency " + todrCurrcode + " (organization: " + porOrgacode + "). Available: " + formatAvailableCurrencies(exchangeRateModelList) + )); + } + + private boolean currencyCodeMatches(String storedCode, String requestedCode) { + if (requestedCode == null) return storedCode == null; + if (storedCode == null) return false; + return storedCode.trim().equals(requestedCode.trim()); + } + + private String normalizeCurrencyCode(String currencyCode, List exchangeRates) { + if (currencyCode == null) return null; + String raw = currencyCode.trim(); + if (raw.isEmpty()) return raw; + String upper = raw.toUpperCase(Locale.ROOT); + + if (exchangeRates != null && exchangeRates.stream().anyMatch(r -> upper.equals(safeTrimUpper(r.getPcrCurrcode())))) { + return upper; + } + + if (exchangeRates != null) { + Optional byShort = exchangeRates.stream() + .filter(r -> upper.equals(safeTrimUpper(r.getPcrCurrshort()))) + .map(ExchangeRateModel::getPcrCurrcode) + .filter(Objects::nonNull) + .findFirst(); + if (byShort.isPresent()) { + return byShort.get(); + } + } + + if (upper.length() == 3 && upper.chars().allMatch(Character::isLetter)) { + try { + int numeric = Currency.getInstance(upper).getNumericCode(); + String numericStr = String.valueOf(numeric); + if (exchangeRates != null && exchangeRates.stream().anyMatch(r -> numericStr.equals(safeTrimUpper(r.getPcrCurrcode())))) { + return numericStr; + } + } catch (Exception ignored) { + } + } + + if (upper.chars().allMatch(Character::isDigit)) { + try { + int numeric = Integer.parseInt(upper); + for (Currency c : Currency.getAvailableCurrencies()) { + if (c.getNumericCode() == numeric) { + String alpha = c.getCurrencyCode(); + if (exchangeRates != null && exchangeRates.stream().anyMatch(r -> alpha.equals(safeTrimUpper(r.getPcrCurrcode())))) { + return alpha; + } + break; + } + } + } catch (Exception ignored) { + } + } + + return upper; + } + + private String safeTrimUpper(String s) { + return s == null ? null : s.trim().toUpperCase(Locale.ROOT); + } + + private String formatAvailableCurrencies(List exchangeRates) { + if (exchangeRates == null || exchangeRates.isEmpty()) return "[]"; + return exchangeRates.stream() + .map(r -> { + String shortCode = (r.getPcrCurrshort() == null || r.getPcrCurrshort().isBlank()) ? "?" : r.getPcrCurrshort().trim(); + String code = (r.getPcrCurrcode() == null || r.getPcrCurrcode().isBlank()) ? "?" : r.getPcrCurrcode().trim(); + return shortCode + "(" + code + ")"; + }) + .distinct() + .limit(20) + .toList() + .toString(); } public Map glAccountTransaction(GLAccontTranasctionRequestModel glAccontTranasctionRequestModel) {