Skip to content

Commit

Permalink
MODFQMMGR-202: Include system exchange rate in Lists app (#218)
Browse files Browse the repository at this point in the history
* MODFQMMGR-202: Include system exchange rate in Lists app

* Update POL definition in 1.2.0
  • Loading branch information
bvsharp committed Mar 29, 2024
1 parent d7c04f0 commit 7c75623
Show file tree
Hide file tree
Showing 17 changed files with 927 additions and 125 deletions.
14 changes: 13 additions & 1 deletion descriptors/ModuleDescriptor-template.json
Expand Up @@ -180,7 +180,11 @@
"permissionName": "fqm.materializedViews.post",
"displayName": "FQM - Refresh materialized views",
"description": "Refresh FQM materialized views",
"visible": true
"visible": true,
"subPermissions": [
"configuration.entries.collection.get",
"finance.exchange-rate.item.get"
]
},
{
"permissionName": "fqm.query.all",
Expand Down Expand Up @@ -298,6 +302,14 @@
{
"id": "statistical-codes",
"version": "1.0"
},
{
"id": "configuration",
"version": "2.0"
},
{
"id": "finance.exchange-rate",
"version": "1.0"
}
],
"launchDescriptor": {
Expand Down
162 changes: 162 additions & 0 deletions src/main/java/org/folio/fqm/repository/DataRefreshRepository.java
@@ -0,0 +1,162 @@
package org.folio.fqm.repository;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jayway.jsonpath.DocumentContext;
import com.jayway.jsonpath.JsonPath;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.folio.fqm.client.SimpleHttpClient;
import org.jooq.DSLContext;
import org.jooq.Field;
import org.jooq.Record2;
import org.jooq.impl.DSL;
import org.springframework.stereotype.Repository;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import static org.jooq.impl.DSL.field;
import static org.jooq.impl.DSL.table;

@Repository
@RequiredArgsConstructor
@Log4j2
public class DataRefreshRepository {

public static final Field<String> CURRENCY_FIELD = field("currency", String.class);
public static final Field<Double> EXCHANGE_RATE_FIELD = field("exchange_rate", Double.class);

private static final String REFRESH_MATERIALIZED_VIEW_SQL = "REFRESH MATERIALIZED VIEW CONCURRENTLY ";
private static final String GET_EXCHANGE_RATE_PATH = "finance/exchange-rate";
private static final String GET_LOCALE_SETTINGS_PATH = "configurations/entries";
private static final Map<String, String> GET_LOCALE_SETTINGS_PARAMS = Map.of(
"query", "(module==ORG and configName==localeSettings)"
);

private static final List<String> MATERIALIZED_VIEW_NAMES = List.of(
"drv_circulation_loan_status",
"drv_inventory_item_status",
"drv_pol_payment_status",
"drv_pol_receipt_status",
"drv_inventory_statistical_code_full",
"drv_languages"
);

static final List<String> SYSTEM_SUPPORTED_CURRENCIES = List.of(
"USD",
"JPY",
"BGN",
"CZK",
"DKK",
"GBP",
"HUF",
"PLN",
"RON",
"SEK",
"CHF",
"ISK",
"NOK",
"HRK",
"RUB",
"TRY",
"AUD",
"BRL",
"CAD",
"CNY",
"HKD",
"IDR",
"ILS",
"INR",
"KRW",
"MXN",
"MYR",
"NZD",
"PHP",
"SGD",
"THB",
"ZAR"
);

private final DSLContext jooqContext;

private final SimpleHttpClient simpleHttpClient;

public void refreshMaterializedViews(String tenantId) {
for (String matViewName : MATERIALIZED_VIEW_NAMES) {
String fullName = tenantId + "_mod_fqm_manager." + matViewName;
log.info("Refreshing materialized view {}", fullName);
jooqContext.execute(REFRESH_MATERIALIZED_VIEW_SQL + fullName);
}
}

public void refreshExchangeRates(String tenantId) {
log.info("Refreshing exchange rates");
String fullTableName = tenantId + "_mod_fqm_manager.currency_exchange_rates";
String systemCurrency = getSystemCurrencyCode();
if (!SYSTEM_SUPPORTED_CURRENCIES.contains(systemCurrency)) {
log.info("System currency does not support automatic exchange rate calculation");
return;
}

List<Record2<String, Double>> exchangeRates = new ArrayList<>();
for (String currency : SYSTEM_SUPPORTED_CURRENCIES) {
Double exchangeRate = getExchangeRate(currency, systemCurrency);
if (!Double.isNaN(exchangeRate)) {
Record2<String, Double> currencyExchangeRate = jooqContext
.newRecord(CURRENCY_FIELD, EXCHANGE_RATE_FIELD)
.value1(currency)
.value2(exchangeRate);
exchangeRates.add(currencyExchangeRate);
}
}

jooqContext.insertInto(table(fullTableName), CURRENCY_FIELD, EXCHANGE_RATE_FIELD)
.valuesOfRecords(exchangeRates)
.onConflict(CURRENCY_FIELD)
.doUpdate()
.set(EXCHANGE_RATE_FIELD, DSL.field("EXCLUDED." + EXCHANGE_RATE_FIELD.getName(), Double.class))
.execute();
}

private String getSystemCurrencyCode() {
log.info("Getting system currency");
try {
String localeSettingsResponse = simpleHttpClient.get(GET_LOCALE_SETTINGS_PATH, GET_LOCALE_SETTINGS_PARAMS);
ObjectMapper objectMapper = new ObjectMapper();
JsonNode localeSettingsNode = objectMapper.readTree(localeSettingsResponse);
String valueString = localeSettingsNode
.path("configs")
.get(0)
.path("value")
.asText();
JsonNode valueNode = objectMapper.readTree(valueString);
return valueNode.path("currency").asText();
} catch (Exception e) {
log.info("No system currency defined, defaulting to USD");
return "USD";
}
}

private Double getExchangeRate(String fromCurrency, String toCurrency) {
log.info("Getting currency exchange rate from {} to {}", fromCurrency, toCurrency);
Map<String, String> exchangeRateParams = Map.of(
"from", fromCurrency,
"to", toCurrency
);
try {
String exchangeRateResponse = simpleHttpClient.get(GET_EXCHANGE_RATE_PATH, exchangeRateParams);
DocumentContext exchangeRateInfo = JsonPath.parse(exchangeRateResponse);
var exchangeRate = exchangeRateInfo.read("exchangeRate");
if (exchangeRate instanceof BigDecimal bd) {
return bd.doubleValue();
}
return (Double) exchangeRate;
} catch (Exception e) {
log.info("Failed to get exchange rate from {} to {}", fromCurrency, toCurrency);
return Double.NaN;
}
}
}

This file was deleted.

@@ -1,21 +1,21 @@
package org.folio.fqm.resource;

import lombok.RequiredArgsConstructor;
import org.folio.fqm.service.MaterializedViewRefreshService;
import org.folio.fqm.service.DataRefreshService;
import org.folio.spring.FolioExecutionContext;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
public class MaterializedViewRefreshController implements MaterializedViewsApi {
public class DataRefreshController implements MaterializedViewsApi {
private final FolioExecutionContext executionContext;
private final MaterializedViewRefreshService materializedViewRefreshService;
private final DataRefreshService dataRefreshService;

@Override
public ResponseEntity<Void> refreshMaterializedViews() {
materializedViewRefreshService.refreshMaterializedViews(executionContext.getTenantId());
public ResponseEntity<Void> refreshData() {
dataRefreshService.refreshData(executionContext.getTenantId());
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
16 changes: 16 additions & 0 deletions src/main/java/org/folio/fqm/service/DataRefreshService.java
@@ -0,0 +1,16 @@
package org.folio.fqm.service;

import lombok.RequiredArgsConstructor;
import org.folio.fqm.repository.DataRefreshRepository;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class DataRefreshService {
private final DataRefreshRepository dataRefreshRepository;

public void refreshData(String tenantId) {
dataRefreshRepository.refreshMaterializedViews(tenantId);
dataRefreshRepository.refreshExchangeRates(tenantId);
}
}

This file was deleted.

1 change: 1 addition & 0 deletions src/main/resources/db/changelog/changelog-master.xml
Expand Up @@ -10,5 +10,6 @@
<include file="changes/v1.0.3/changelog-v1.0.3.xml" relativeToChangelogFile="true"/>
<include file="changes/v1.0.4/changelog-v1.0.4.xml" relativeToChangelogFile="true"/>
<include file="changes/v1.1.0/changelog-v1.1.0.xml" relativeToChangelogFile="true"/>
<include file="changes/v1.1.1/changelog-v1.1.1.xml" relativeToChangelogFile="true"/>
<include file="changes/v1.2.0/changelog-v1.2.0.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>
@@ -0,0 +1,11 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd">

<changeSet id="MODFQMMGR-202" author="bsharp@ebsco.com" runOnChange="true">
<sqlFile path="sql/create-table-currency-exchange-rates.sql" relativeToChangelogFile="true"/>
</changeSet>
<include file="update-drv-purchase-order-line-details-definition.xml" relativeToChangelogFile="true"/>

</databaseChangeLog>
@@ -0,0 +1,4 @@
CREATE TABLE IF NOT EXISTS currency_exchange_rates(
currency VARCHAR(8) PRIMARY KEY,
exchange_rate FLOAT
);

0 comments on commit 7c75623

Please sign in to comment.