diff --git a/AUTHORS b/AUTHORS index 77107bd60..05f7ab5c5 100644 --- a/AUTHORS +++ b/AUTHORS @@ -46,7 +46,7 @@ Written in 2015 by Sam Hamilton - samhamilton Written in 2015 by Leonardo Carvalho - CarvalhoLeonardo Written in 2015 by Swapnil M Mane - swapnilmmane Written in 2015 by Anton Akhiar - akhiar -Written in 2015-2018 by Jens Hardings - jenshp +Written in 2015-2023 by Jens Hardings - jenshp Written in 2016 by Shifeng Zhang - zhangshifeng Written in 2016 by Scott Gray - lektran Written in 2016 by Mark Haney - mphaney @@ -94,7 +94,7 @@ Written in 2015 by Jimmy Shen - shendepu Written in 2015-2016 by Sam Hamilton - samhamilton Written in 2015 by Leonardo Carvalho - CarvalhoLeonardo Written in 2015 by Anton Akhiar - akhiar -Written in 2015-2016 by Jens Hardings - jenshp +Written in 2015-2023 by Jens Hardings - jenshp Written in 2016 by Shifeng Zhang - zhangshifeng Written in 2016 by Scott Gray - lektran Written in 2016 by Mark Haney - mphaney diff --git a/framework/entity/BasicEntities.xml b/framework/entity/BasicEntities.xml index 3ca7a10e4..d71b77fc5 100644 --- a/framework/entity/BasicEntities.xml +++ b/framework/entity/BasicEntities.xml @@ -389,7 +389,9 @@ along with this software (see the LICENSE.md file). If not, see - + + + diff --git a/framework/src/main/groovy/org/moqui/impl/context/L10nFacadeImpl.java b/framework/src/main/groovy/org/moqui/impl/context/L10nFacadeImpl.java index acec7f13f..f327eb248 100644 --- a/framework/src/main/groovy/org/moqui/impl/context/L10nFacadeImpl.java +++ b/framework/src/main/groovy/org/moqui/impl/context/L10nFacadeImpl.java @@ -15,6 +15,7 @@ import org.moqui.BaseArtifactException; import org.moqui.context.L10nFacade; +import org.moqui.entity.EntityCondition; import org.moqui.entity.EntityValue; import org.moqui.entity.EntityFind; @@ -23,6 +24,8 @@ import javax.xml.bind.DatatypeConverter; import java.math.BigDecimal; import java.math.RoundingMode; +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.text.NumberFormat; import java.sql.Date; import java.sql.Time; @@ -100,13 +103,14 @@ public String localize(String original, Locale locale) { } @Override - public String formatCurrency(Object amount, String uomId) { return formatCurrency(amount, uomId, null, getLocale()); } + public String formatCurrencyNoSymbol(Object amount, String uomId) { return formatCurrency(amount, uomId, null, getLocale(), true); } @Override - public String formatCurrency(Object amount, String uomId, Integer fractionDigits) { - return formatCurrency(amount, uomId, fractionDigits, getLocale()); - } + public String formatCurrency(Object amount, String uomId) { return formatCurrency(amount, uomId, null, getLocale(), false); } + @Override + public String formatCurrency(Object amount, String uomId, Integer fractionDigits) { return formatCurrency(amount, uomId, fractionDigits, getLocale(), false); } @Override - public String formatCurrency(Object amount, String uomId, Integer fractionDigits, Locale locale) { + public String formatCurrency(Object amount, String uomId, Integer fractionDigits, Locale locale) { return formatCurrency(amount, uomId, fractionDigits, locale, false); } + public String formatCurrency(Object amount, String uomId, Integer fractionDigits, Locale locale, boolean hideSymbol) { if (amount == null) return ""; if (amount instanceof CharSequence) { if (((CharSequence) amount).length() == 0) { @@ -116,30 +120,53 @@ public String formatCurrency(Object amount, String uomId, Integer fractionDigits } } + if (locale == null) locale = getLocale(); + NumberFormat nf = NumberFormat.getCurrencyInstance(locale); + String currencySymbol = null; + if (hideSymbol) + currencySymbol = ""; + EntityValue uom = null; + if (uomId != null && uomId.length() > 0) { + List uomList = eci.getEntity().find("moqui.basic.Uom").condition("uomId", uomId).condition("uomTypeEnumId", "UT_CURRENCY_MEASURE").list(); + if (uomList.size() > 0) { + uom = uomList.get(0); + String symbol = uom.getString("symbol"); + if (currencySymbol == null && symbol != null) + currencySymbol = symbol; + Object fractionDigitsField = uom.get("fractionDigits"); + if (fractionDigits == null && fractionDigitsField != null) { + if (fractionDigitsField instanceof Integer) + fractionDigits = (Integer)fractionDigitsField; + else if (fractionDigitsField instanceof Long) + fractionDigits = ((Long)fractionDigitsField).intValue(); + } + } + } + Currency currency = null; if (uomId != null && uomId.length() > 0) { try { currency = Currency.getInstance(uomId); + if (currencySymbol == null) + currencySymbol = currency.getSymbol(); + if (fractionDigits == null) + fractionDigits = currency.getDefaultFractionDigits(); } catch (Exception e) { if (logger.isTraceEnabled()) logger.trace("Ignoring IllegalArgumentException for Currency parse: " + e.toString()); } } - - if (locale == null) locale = getLocale(); - if (currency != null) { - NumberFormat nf = NumberFormat.getCurrencyInstance(locale); - nf.setCurrency(currency); - if (fractionDigits == null) fractionDigits = currency.getDefaultFractionDigits(); - nf.setMaximumFractionDigits(fractionDigits); - nf.setMinimumFractionDigits(fractionDigits); - return nf.format(amount); - } else { - NumberFormat nf = NumberFormat.getInstance(); - if (fractionDigits == null) fractionDigits = 2; - nf.setMaximumFractionDigits(fractionDigits); - nf.setMinimumFractionDigits(fractionDigits); - return nf.format(amount); - } + if (currencySymbol == null) + currencySymbol = ""; + + if (fractionDigits == null) + fractionDigits = 2; + nf.setMaximumFractionDigits(fractionDigits); + nf.setMinimumFractionDigits(fractionDigits); + DecimalFormatSymbols dfSymbols = new DecimalFormatSymbols(locale); + dfSymbols.setCurrencySymbol(currencySymbol); + ((DecimalFormat)nf).setDecimalFormatSymbols(dfSymbols); + + return nf.format(amount); } @Override @@ -152,10 +179,29 @@ public BigDecimal roundCurrency(BigDecimal amount, String uomId, boolean precise } @Override public BigDecimal roundCurrency(BigDecimal amount, String uomId, boolean precise, RoundingMode mode) { - Currency currency = Currency.getInstance(uomId); - int nDigits = currency.getDefaultFractionDigits(); - if (precise) nDigits++; - return amount.setScale(nDigits, mode); + if (amount == null) + return null; + List uomList = eci.getEntity().find("moqui.basic.Uom").condition("uomId", uomId).condition("uomTypeEnumId", "UT_CURRENCY_MEASURE").list(); + Integer fractionDigits = null; + if (uomList.size() > 0) { + Object fractionDigitsField = uomList.get(0).get("fractionDigits"); + if (fractionDigitsField != null) { + if (fractionDigitsField instanceof Integer) + fractionDigits = (Integer)fractionDigitsField; + else if (fractionDigitsField instanceof Long) + fractionDigits = ((Long)fractionDigitsField).intValue(); + } + } + if (fractionDigits == null) { + Currency currency = Currency.getInstance(uomId); + fractionDigits = currency.getDefaultFractionDigits(); + } + if (fractionDigits == null) { + fractionDigits = 2; + } + if (precise) fractionDigits++; + eci.getLogger().info("Rounding to " + fractionDigits + " digits."); + return amount.setScale(fractionDigits, mode); } @Override diff --git a/framework/src/main/groovy/org/moqui/impl/screen/ScreenRenderImpl.groovy b/framework/src/main/groovy/org/moqui/impl/screen/ScreenRenderImpl.groovy index 58518ed74..eb40bf57d 100644 --- a/framework/src/main/groovy/org/moqui/impl/screen/ScreenRenderImpl.groovy +++ b/framework/src/main/groovy/org/moqui/impl/screen/ScreenRenderImpl.groovy @@ -1823,6 +1823,7 @@ class ScreenRenderImpl implements ScreenRender { String fieldValue = (String) null String textAttr = widgetNode.attribute("text") String currencyAttr = widgetNode.attribute("currency-unit-field") + String currencyNoSymbolAttr = widgetNode.attribute("currency-hide-symbol") if (textAttr != null && ! textAttr.isEmpty()) { String textMapAttr = widgetNode.attribute("text-map") Map textMap = (Map) null @@ -1833,9 +1834,16 @@ class ScreenRenderImpl implements ScreenRender { } else { fieldValue = ec.resourceFacade.expand(textAttr, null) } - if (currencyAttr != null && !currencyAttr.isEmpty()) - fieldValue = ec.l10nFacade.formatCurrency(fieldValue, ec.resourceFacade.expression(currencyAttr, null) as String) + if (currencyAttr != null && !currencyAttr.isEmpty()) { + if (currencyNoSymbolAttr == "true") + fieldValue = ec.l10nFacade.formatCurrencyNoSymbol(fieldValue, ec.resourceFacade.expression(currencyAttr, null) as String) + else + fieldValue = ec.l10nFacade.formatCurrency(fieldValue, ec.resourceFacade.expression(currencyAttr, null) as String) + } } else if (currencyAttr != null && !currencyAttr.isEmpty()) { + if (currencyNoSymbolAttr == "true") + fieldValue = ec.l10nFacade.formatCurrencyNoSymbol(getFieldValue(fieldNode, ""), ec.resourceFacade.expression(currencyAttr, null) as String) + else fieldValue = ec.l10nFacade.formatCurrency(getFieldValue(fieldNode, ""), ec.resourceFacade.expression(currencyAttr, null) as String) } else { fieldValue = getFieldValueString(widgetNode) diff --git a/framework/src/main/java/org/moqui/context/L10nFacade.java b/framework/src/main/java/org/moqui/context/L10nFacade.java index 235a9cf14..f2597e5d0 100644 --- a/framework/src/main/java/org/moqui/context/L10nFacade.java +++ b/framework/src/main/java/org/moqui/context/L10nFacade.java @@ -41,11 +41,16 @@ public interface L10nFacade { * @param uomId The uomId (ISO currency code), required. * @param fractionDigits Number of digits after the decimal point to display. If null defaults to number defined * by java.util.Currency.defaultFractionDigits() for the specified currency in uomId. + * @param locale Locale to use for formatting. + * @param hideSymbol option to hide the Symbol of the currency and only display the number formatted according + * to locale. * @return The formatted currency amount. */ + String formatCurrency(Object amount, String uomId, Integer fractionDigits, Locale locale, boolean hideSymbol); + String formatCurrency(Object amount, String uomId, Integer fractionDigits, Locale locale); String formatCurrency(Object amount, String uomId, Integer fractionDigits); String formatCurrency(Object amount, String uomId); - String formatCurrency(Object amount, String uomId, Integer fractionDigits, Locale locale); + String formatCurrencyNoSymbol(Object amount, String uomId); /** Round currency according to the currency's specified amount of digits and rounding method. * @param amount The amount in BigDecimal to be rounded. diff --git a/framework/xsd/xml-form-3.xsd b/framework/xsd/xml-form-3.xsd index 3e356d47d..c2fe0937b 100644 --- a/framework/xsd/xml-form-3.xsd +++ b/framework/xsd/xml-form-3.xsd @@ -897,6 +897,10 @@ along with this software (see the LICENSE.md file). If not, see Specifies the currency uomId (ISO code) used to format the value. Will only format as currency if this is specified. + + When currency-unit-field has value, defines whether currency symbol will be + hidden or displayed normally. + Used to format the output of Number/Time/Date/Timestamp/etc objects. With auto-fields-service will inherit from service parameter.