You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by nm...@apache.org on 2019/08/09 20:37:12 UTC

svn commit: r1864832 - in /ofbiz/ofbiz-framework/trunk: applications/accounting/config/ applications/order/template/order/ applications/order/widget/ordermgr/ framework/base/src/main/java/org/apache/ofbiz/base/util/ framework/common/config/ framework/w...

Author: nmalin
Date: Fri Aug  9 20:37:11 2019
New Revision: 1864832

URL: http://svn.apache.org/viewvc?rev=1864832&view=rev
Log:
Implemented: Homogenize displaying number with multiple format
(OFBIZ-7532)

To display a number we had different possibilities :
 * on ftl use the template <@ofbizAmount and <@ofbizCurrency
 * by java call a function UtilFormatOut.formatAmount, UtilFormatOut.formatPrice, UtilFormatOut.formatQuantity, etc..
 * by form widget, use <display type=accounting-number for accounting but nothing for other

To simplify and homogenize all, I implemented a number type purpose :
   * default: display a number by default, use when no purpose is present
   * quantity: display a number as a quantity
   * amount: display a number as an amount (like price without currency)
   * spelled: litteral displaying for a number (use on <@ofbizAmount ftl only before)
   * percentage: display a number as a percentage
   * accounting: diplay a number for accounting specific

Each purpose can be associate to a number for displaying it :
   * on ftl <@ofbizNumber number=value format=purpose/>
   * on java UtilFormatOut.formatNumber(value, purpose, delegator, locale)
   * on form widget <display type=number format=purpose/>

The format use by a purpose is define on framework/common/config/number.properties with the template
    .displaying.format = ##0.00

With this, you can surchage a configuration, create your own purpose or surchage only one through entity SystemProperty.

Concerning the backware compatibility: 
 * For the ftl the template <@ofbizAmount is now a link to '<@ofbizNumber format=amount'
 * For java all previous function call UtilFormatOut.formatNumber with the matching purpose
 * For form xml accounting-number is managed as an exection

Last point, display a currency is different that a number, so I didn't refactoring some code for this case (only move properties from general to number for centralize de configuration on the same file)

Thanks Charles Steltzlen to start the refactoring

Added:
    ofbiz/ofbiz-framework/trunk/framework/common/config/number.properties   (with props)
    ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizNumberTransform.java   (with props)
Modified:
    ofbiz/ofbiz-framework/trunk/applications/accounting/config/arithmetic.properties
    ofbiz/ofbiz-framework/trunk/applications/order/template/order/EditOrderItems.ftl
    ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/FieldLookupForms.xml
    ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/OrderEntryForms.xml
    ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/QuoteForms.xml
    ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/ReportForms.xml
    ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/RequirementForms.xml
    ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilFormatOut.java
    ofbiz/ofbiz-framework/trunk/framework/common/config/general.properties
    ofbiz/ofbiz-framework/trunk/framework/webapp/config/freemarkerTransforms.properties
    ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizAmountTransform.java
    ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizCurrencyTransform.java
    ofbiz/ofbiz-framework/trunk/framework/webtools/widget/MiscForms.xml
    ofbiz/ofbiz-framework/trunk/framework/widget/dtd/widget-form.xsd
    ofbiz/ofbiz-framework/trunk/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelFormField.java

Modified: ofbiz/ofbiz-framework/trunk/applications/accounting/config/arithmetic.properties
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/applications/accounting/config/arithmetic.properties?rev=1864832&r1=1864831&r2=1864832&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/applications/accounting/config/arithmetic.properties (original)
+++ ofbiz/ofbiz-framework/trunk/applications/accounting/config/arithmetic.properties Fri Aug  9 20:37:11 2019
@@ -49,6 +49,3 @@ finaccount.roundingSimpleMethod = HalfUp
 salestax.calc.decimals = 3
 salestax.final.decimals = 2
 salestax.rounding = ROUND_HALF_UP
-
-# the default accounting-number format for negatives in parentheses
-accounting-number.format = #,##0.00;(#,##0.00)

Modified: ofbiz/ofbiz-framework/trunk/applications/order/template/order/EditOrderItems.ftl
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/applications/order/template/order/EditOrderItems.ftl?rev=1864832&r1=1864831&r2=1864832&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/applications/order/template/order/EditOrderItems.ftl (original)
+++ ofbiz/ofbiz-framework/trunk/applications/order/template/order/EditOrderItems.ftl Fri Aug  9 20:37:11 2019
@@ -175,7 +175,7 @@ under the License.
                               <td class="align-text" valign="top" nowrap="nowrap">
                                   <#-- check for permission to modify price -->
                                   <#if (allowPriceChange) && !("ITEM_CANCELLED" == orderItem.statusId || "ITEM_COMPLETED" == orderItem.statusId)>
-                                      <input type="text" size="8" name="ipm_${orderItem.orderItemSeqId}" value="<@ofbizAmount amount=orderItem.unitPrice/>"/>
+                                      <input type="text" size="8" name="ipm_${orderItem.orderItemSeqId}" value="<@ofbizNumber number=orderItem.unitPrice format="quantity"/>"/>
                                       &nbsp;<input type="checkbox" name="opm_${orderItem.orderItemSeqId}" value="Y"/>
                                   <#else>
                                       <div><@ofbizCurrency amount=orderItem.unitPrice isoCode=currencyUomId/> / <@ofbizCurrency amount=orderItem.unitListPrice isoCode=currencyUomId/></div>

Modified: ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/FieldLookupForms.xml
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/FieldLookupForms.xml?rev=1864832&r1=1864831&r2=1864832&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/FieldLookupForms.xml (original)
+++ ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/FieldLookupForms.xml Fri Aug  9 20:37:11 2019
@@ -497,7 +497,7 @@ under the License.
         </field>
         <field name="requirementStartDate" title="${uiLabelMap.OrderRequirementStartDate}"><display/></field>
         <field name="requiredByDate" title="${uiLabelMap.OrderRequirementByDate}"><display/></field>
-        <field name="quantity" title="${uiLabelMap.CommonQuantity}"><display/></field>
+        <field name="quantity" title="${uiLabelMap.CommonQuantity}"><display type="number" format="quantity"/></field>
     </form>
 
     <form name="LookupShoppingList" target="LookupShoppingList" title="" type="single"

Modified: ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/OrderEntryForms.xml
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/OrderEntryForms.xml?rev=1864832&r1=1864831&r2=1864832&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/OrderEntryForms.xml (original)
+++ ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/OrderEntryForms.xml Fri Aug  9 20:37:11 2019
@@ -189,11 +189,11 @@ under the License.
         <field name="supplierProductId"><display/></field>
         <field name="supplierProductName"><display/></field>
         <field name="internalName"><display/></field>
-        <field name="lastPrice"><display/></field>
-        <field name="minimumOrderQuantity"><display/></field>
-        <field name="minimumStock"><display/></field>
+        <field name="lastPrice"><display type="number" format="amount"/></field>
+        <field name="minimumOrderQuantity"><display type="number" format="quantity"/></field>
+        <field name="minimumStock"><display type="number" format="quantity"/></field>
         <field name="qohAtp" title="${uiLabelMap.ProductAtpQoh}"><display/></field>
-        <field name="quantityOnOrder"><display/></field>
+        <field name="quantityOnOrder"><display type="number" format="quantity"/></field>
         <field name="quantity" title="${uiLabelMap.OrderQuantity}">
             <text size="5" maxlength="10"/>
         </field>

Modified: ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/QuoteForms.xml
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/QuoteForms.xml?rev=1864832&r1=1864831&r2=1864832&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/QuoteForms.xml (original)
+++ ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/QuoteForms.xml Fri Aug  9 20:37:11 2019
@@ -212,7 +212,7 @@ under the License.
         </field>
         <field name="workEffortId" title="${uiLabelMap.OrderOrderQuoteWorkEffortId}"></field>
         <field name="quantity" title="${uiLabelMap.CommonQuantity}"></field>
-        <field name="quoteUnitPrice" title="${uiLabelMap.OrderOrderQuoteUnitPrice}"></field>
+        <field name="quoteUnitPrice" title="${uiLabelMap.OrderOrderQuoteUnitPrice}"><display type="number" format="amount"/></field>
         <field name="estimatedDeliveryDate" title="${uiLabelMap.OrderOrderQuoteEstimatedDeliveryDate}"></field>
         <field name="productFeatureId"><hidden/></field>
         <field name="skillTypeId"><hidden/></field>
@@ -267,8 +267,8 @@ under the License.
             </drop-down>
         </field>
         <field name="workEffortId" title="${uiLabelMap.OrderOrderQuoteWorkEffortId}"></field>
-        <field name="quantity" title="${uiLabelMap.CommonQuantity}"></field>
-        <field name="quoteUnitPrice" title="${uiLabelMap.OrderOrderQuoteUnitPrice}"></field>
+        <field name="quantity" title="${uiLabelMap.CommonQuantity}"><display type="number" format="quantity"/></field>
+        <field name="quoteUnitPrice" title="${uiLabelMap.OrderOrderQuoteUnitPrice}"><display type="number" format="amount"/></field>
         <field name="estimatedDeliveryDate" title="${uiLabelMap.OrderOrderQuoteEstimatedDeliveryDate}"></field>
         <field name="comments" title="${uiLabelMap.CommonComments}"></field>
         <!--
@@ -358,11 +358,11 @@ under the License.
         <field name="productId" title="${uiLabelMap.ProductProductId}">
             <display-entity entity-name="Product" key-field-name="productId" description="${productId} - ${internalName}"/>
         </field>
-        <field name="quantity" title="${uiLabelMap.CommonQuantity}"><display/></field>
-        <field name="averageCost" title="${uiLabelMap.OrderOrderQuoteAverageCost}"><display/></field>
-        <field name="costToPriceMult" title="${uiLabelMap.OrderOrderQuoteCostToPrice}"><display/></field>
-        <field name="defaultQuoteUnitPrice" title="${uiLabelMap.OrderOrderQuoteDefaultUnitPrice}"><display/></field>
-        <field name="quoteUnitPrice" title="${uiLabelMap.OrderOrderQuoteUnitPrice}"><display/></field>
+        <field name="quantity" title="${uiLabelMap.CommonQuantity}"><display type="number" format="quantity"/></field>
+        <field name="averageCost" title="${uiLabelMap.OrderOrderQuoteAverageCost}"><display type="number" format="amount"/></field>
+        <field name="costToPriceMult" title="${uiLabelMap.OrderOrderQuoteCostToPrice}"><display type="number" format="amount"/></field>
+        <field name="defaultQuoteUnitPrice" title="${uiLabelMap.OrderOrderQuoteDefaultUnitPrice}"><display type="number" format="amount"/></field>
+        <field name="quoteUnitPrice" title="${uiLabelMap.OrderOrderQuoteUnitPrice}"><display type="number" format="amount"/></field>
         <field name="manualQuoteUnitPrice" title="${uiLabelMap.OrderOrderQuoteManualUnitPrice}"><text/></field>
         <field name="_rowSubmit" title="${uiLabelMap.CommonSelected}"><check/></field>
         <field name="submitButton" title="${uiLabelMap.CommonSubmit}" use-when="quoteItemAndCostInfos!=[]" widget-style="smallSubmit"><submit/></field>
@@ -447,9 +447,9 @@ under the License.
         <field name="productId" title="${uiLabelMap.ProductProductId}">
             <display-entity entity-name="Product" key-field-name="productId" description="${productId} - ${internalName}"/>
         </field>
-        <field name="quantity" title="${uiLabelMap.CommonQuantity}"><display/></field>
+        <field name="quantity" title="${uiLabelMap.CommonQuantity}"><display type="number" format="quantity"/></field>
         <field name="averageCost" title="${uiLabelMap.OrderOrderQuoteAverageCost}"><display/></field>
-        <field name="quoteUnitPrice" title="${uiLabelMap.OrderOrderQuoteUnitPrice}"><display/></field>
+        <field name="quoteUnitPrice" title="${uiLabelMap.OrderOrderQuoteUnitPrice}"><display type="number" format="amount"/></field>
         <field name="profit" title="${uiLabelMap.OrderOrderQuoteProfit}"><display/></field>
         <field name="percProfit" title="${uiLabelMap.OrderOrderQuotePercProfit}"><display/></field>
     </form>

Modified: ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/ReportForms.xml
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/ReportForms.xml?rev=1864832&r1=1864831&r2=1864832&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/ReportForms.xml (original)
+++ ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/ReportForms.xml Fri Aug  9 20:37:11 2019
@@ -129,7 +129,7 @@ under the License.
     <!-- list open order items -->
     <form name="OpenOrderItemsList" type="list" list-name="orderItemList"
         odd-row-style="alternate-row" default-table-style="basic-table hover-bar" paginate-target="OpenOrderItemsReport">
-        <field name="orderDate" title="${uiLabelMap.OrderDate}"><display/></field>
+        <field name="orderDate" title="${uiLabelMap.OrderDate}"><display type="date-time"/></field>
         <field name="orderId" title="${uiLabelMap.OrderOrderId}" widget-style="buttontext">
             <hyperlink description="${orderId}" target="orderview">
                 <parameter param-name="orderId"/>
@@ -137,29 +137,29 @@ under the License.
         </field>
         <field name="productId" title="${uiLabelMap.ProductProduct}"><display/></field>
         <field name="itemDescription" title="${uiLabelMap.CommonDescription}"><display/></field>
-        <field name="quantityOrdered" title="${uiLabelMap.ProductQuantity}"><display/></field>
-        <field name="quantityIssued" title="${uiLabelMap.OrderQtyShipped}"><display/></field>
-        <field name="quantityOpen" title="${uiLabelMap.ProductOpenQuantity}"><display/></field>
-        <field name="shipAfterDate" title="${uiLabelMap.OrderShipAfterDate}"><display/></field>
-        <field name="shipBeforeDate" title="${uiLabelMap.OrderShipBeforeDate}"><display/></field>
+        <field name="quantityOrdered" title="${uiLabelMap.ProductQuantity}"><display type="number" format="quantity"/></field>
+        <field name="quantityIssued" title="${uiLabelMap.OrderQtyShipped}"><display type="number" format="quantity"/></field>
+        <field name="quantityOpen" title="${uiLabelMap.ProductOpenQuantity}"><display type="number" format="quantity"/></field>
+        <field name="shipAfterDate" title="${uiLabelMap.OrderShipAfterDate}"><display type="date-time"/></field>
+        <field name="shipBeforeDate" title="${uiLabelMap.OrderShipBeforeDate}"><display type="date-time"/></field>
         <field name="comments" title="${uiLabelMap.CommonComments}"><display/></field>
-        <field name="costPrice" title="${uiLabelMap.ProductCostPrice}"><display/></field>
-        <field name="listPrice" title="${uiLabelMap.ProductListPrice}"><display/></field>
-        <field name="retailPrice" title="${uiLabelMap.ProductRetailPrice}"><display/></field>
-        <field name="discount" title="${uiLabelMap.ProductDiscount}"><display/></field>
+        <field name="costPrice" title="${uiLabelMap.ProductCostPrice}"><display type="number" format="amount"/></field>
+        <field name="listPrice" title="${uiLabelMap.ProductListPrice}"><display type="number" format="amount"/></field>
+        <field name="retailPrice" title="${uiLabelMap.ProductRetailPrice}"><display type="number" format="amount"/></field>
+        <field name="discount" title="${uiLabelMap.ProductDiscount}"><display type="number" format="amount"/></field>
         <field name="calculatedMarkup" title="${uiLabelMap.OrderCalculatedMarkup}"><display/></field>
         <field name="percentMarkup" title="${uiLabelMap.OrderPercentageMarkup}"><display/></field>
     </form>
 
     <form name="OpenOrderItemsTotal" type="list" list-name="totalAmountList"
         odd-row-style="alternate-row" default-table-style="basic-table hover-bar">
-        <field name="total" title="${uiLabelMap.CommonTotal}"><display/></field>
-        <field name="totalQuantityOrdered"><display/></field>
-        <field name="totalQuantityOpen"><display/></field>
-        <field name="totalCostPrice"><display/></field>
-        <field name="totalListPrice"><display/></field>
-        <field name="totalRetailPrice"><display/></field>
-        <field name="totalDiscount"><display/></field>
+        <field name="total" title="${uiLabelMap.CommonTotal}"><display type="number" format="quantity"/></field>
+        <field name="totalQuantityOrdered"><display type="number" format="quantity"/></field>
+        <field name="totalQuantityOpen"><display type="number" format="quantity"/></field>
+        <field name="totalCostPrice"><display type="number" format="amount"/></field>
+        <field name="totalListPrice"><display type="number" format="amount"/></field>
+        <field name="totalRetailPrice"><display type="number" format="amount"/></field>
+        <field name="totalDiscount"><display type="number" format="amount"/></field>
         <field name="totalMarkup"><display/></field>
         <field name="totalPercentMarkup"><display/></field>
     </form>

Modified: ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/RequirementForms.xml
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/RequirementForms.xml?rev=1864832&r1=1864831&r2=1864832&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/RequirementForms.xml (original)
+++ ofbiz/ofbiz-framework/trunk/applications/order/widget/ordermgr/RequirementForms.xml Fri Aug  9 20:37:11 2019
@@ -91,9 +91,9 @@ under the License.
         </field>
         <field name="requirementStartDate"><display/></field>
         <field name="requiredByDate"><display/></field>
-        <field name="quantity" title="${uiLabelMap.CommonQuantity}"><display/></field>
-        <field name="facilityQuantityOnHandTotal"><display/></field>
-        <field name="quantityOnHandTotal"><display/></field>
+        <field name="quantity" title="${uiLabelMap.CommonQuantity}"><display type="number" format="quantity"/></field>
+        <field name="facilityQuantityOnHandTotal"><display type="number" format="quantity"/></field>
+        <field name="quantityOnHandTotal"><display type="number" format="quantity"/></field>
         <field name="requestsLink" widget-style="buttontext">
             <hyperlink description="${uiLabelMap.OrderRequests}" target="ListRequirementCustRequests">
                 <parameter param-name="requirementId"/>
@@ -249,7 +249,7 @@ under the License.
         </field>
         <field name="requirementStartDate"><display/></field>
         <field name="requiredByDate"><display/></field>
-        <field name="quantity" title="${uiLabelMap.CommonQuantity}"><display/></field>
+        <field name="quantity" title="${uiLabelMap.CommonQuantity}"><display type="number" format="quantity"/></field>
         <field name="_rowSubmit" title="${uiLabelMap.CommonSelect}"><check/></field>
         <field name="submitButton" title="${uiLabelMap.CommonSubmit}" widget-style="smallSubmit">
           <submit/>
@@ -322,10 +322,10 @@ under the License.
             <hyperlink description="${supplierProductId}" target="/catalog/control/EditProductSuppliers?productId=${productId}" target-type="inter-app"/>
         </field>
         <field name="idValue" title="${uiLabelMap.ProductUPCA}"><display/></field>
-        <field name="minimumOrderQuantity" widget-area-style="align-text"><display/></field>
+        <field name="minimumOrderQuantity" widget-area-style="align-text"><display type="number" format="quantity"/></field>
         <field name="lastPrice" widget-area-style="align-text"><display type="currency" currency="${currencyUomId}"/></field>
         <field name="requiredByDate"><display/></field>
-        <field name="quantity" widget-area-style="align-text"><display/></field>
+        <field name="quantity" widget-area-style="align-text"><display type="number" format="quantity"/></field>
         <field name="comments"><display/></field>
     </form>
     <form name="ApprovedProductRequirements" type="multi" use-row-submit="true" target="quickPurchaseOrderEntry" title="" list-name="requirementsForSupplier"
@@ -353,7 +353,7 @@ under the License.
         <field name="supplierCurrencyUomId" entry-name="parameters.supplierCurrencyUomId"><display/></field>
         <field name="supplierProductId" title="${uiLabelMap.ProductSupplierProductId}"><display/></field>
         <field name="idValue" title="${uiLabelMap.ProductUPCA}"><display/></field>
-        <field name="minimumOrderQuantity" title="${uiLabelMap.FormFieldTitle_minimumOrderQuantity}" widget-area-style="align-text"><display/></field>
+        <field name="minimumOrderQuantity" title="${uiLabelMap.FormFieldTitle_minimumOrderQuantity}" widget-area-style="align-text"><display type="number" format="quantity"/></field>
         <field name="lastPrice" widget-area-style="align-text"><display type="currency" currency="${currencyUomId}"/></field>
         <field name="requiredByDate"><display/></field>
         <field name="atp" title="${uiLabelMap.ProductAtp}" widget-area-style="align-text"><display/></field>
@@ -373,7 +373,7 @@ under the License.
     <form name="ApprovedProductRequirementsSummary" type="single" default-map-name="quantityReport" target="ApprovedProductRequirements"
         header-row-style="header-row" default-table-style="basic-table">
         <field name="distinctProductCount" title="${uiLabelMap.OrderRequirementNumberOfProducts}"><display/></field>
-        <field name="quantityTotal"><display/></field>
+        <field name="quantityTotal"><display type="number" format="quantity"/></field>
         <field name="amountTotal"><display type="currency" currency="${currencyUomId}"/></field>
     </form>
     <form name="ApprovedProductRequirementsByVendor" type="list" use-row-submit="true" list-name="requirements" target="ApprovedProductRequirements" separate-columns="true"

Modified: ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilFormatOut.java
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilFormatOut.java?rev=1864832&r1=1864831&r2=1864832&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilFormatOut.java (original)
+++ ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilFormatOut.java Fri Aug  9 20:37:11 2019
@@ -21,12 +21,12 @@ package org.apache.ofbiz.base.util;
 import java.math.BigDecimal;
 import java.nio.charset.StandardCharsets;
 import java.text.DateFormat;
-import java.text.DecimalFormat;
-import java.text.ParseException;
 import java.util.Base64;
 import java.util.Date;
 import java.util.Locale;
 import java.util.TimeZone;
+import org.apache.ofbiz.entity.Delegator;
+import org.apache.ofbiz.entity.util.EntityUtilProperties;
 
 /**
  * General output formatting functions - mainly for helping in JSPs
@@ -34,16 +34,11 @@ import java.util.TimeZone;
 public final class UtilFormatOut {
 
     public static final String module = UtilFormatOut.class.getName();
-
-    // ------------------- price format handlers -------------------
-    // FIXME: This is not thread-safe! DecimalFormat is not synchronized.
-    private static final DecimalFormat priceDecimalFormat = new DecimalFormat(UtilProperties.getPropertyValue("general", "currency.decimal.format", "#,##0.00"));
-
-    // ------------------- quantity format handlers -------------------
-    private static final DecimalFormat quantityDecimalFormat = new DecimalFormat("#,##0.###");
-
-    // ------------------- percentage format handlers -------------------
-    private static final DecimalFormat percentageDecimalFormat = new DecimalFormat("##0.##%");
+    public static final String DEFAULT_FORMAT = "default";
+    public static final String AMOUNT_FORMAT = "amount";
+    public static final String QUANTITY_FORMAT = "quantity";
+    public static final String PERCENTAGE_FORMAT = "percentage";
+    public static final String SPELLED_OUT_FORMAT = "spelled-out";
 
     private UtilFormatOut() {}
 
@@ -54,43 +49,70 @@ public final class UtilFormatOut {
         return "";
     }
 
-    /** Formats a Double representing a price into a string
-     * @param price The price Double to be formatted
-     * @return A String with the formatted price
+    /** Format a number with format type define by properties
+     *
      */
-    public static String formatPrice(Double price) {
-        if (price == null) {
+    public static String formatNumber(Double number, String formatType, Delegator delegator, Locale locale) {
+        if (number == null) {
             return "";
         }
-        return formatPrice(price.doubleValue());
+        if (formatType == null) {
+            formatType = DEFAULT_FORMAT;
+        }
+        if (locale == null) {
+            locale = Locale.getDefault();
+        }
+
+        //lookup for known specific format
+        if (formatType.equals(SPELLED_OUT_FORMAT)) {
+            return formatSpelledOutAmount(number, locale);
+        }
+
+        //Resolve template to use from formatType
+        String formatTypeKey = formatType + ".displaying.format";
+        String template = delegator != null ?
+                EntityUtilProperties.getPropertyValue("number", formatTypeKey, delegator):
+                UtilProperties.getPropertyValue("number", formatTypeKey);
+        if (UtilValidate.isEmpty(template)) {
+            Debug.logWarning("Number template not found for format " + formatType +
+                    ", please check your property on number for " + formatTypeKey, module);
+            template = delegator != null ?
+                    EntityUtilProperties.getPropertyValue("number", "default.displaying.format", delegator):
+                    UtilProperties.getPropertyValue("number", "default.displaying.format");
+        }
+        if (UtilValidate.isEmpty(template)) {
+            Debug.logWarning("Number template not found for default displaying.format" +
+                    ", please check your property on number for default.displaying.format", module);
+            template = "##0.00";
+        }
+
+        //With the template parse the number to display it
+        return formatDecimalNumber(number, template, locale);
     }
 
-    /** Formats a BigDecimal representing a price into a string
-     * @param price The price BigDecimal to be formatted
-     * @return A String with the formatted price
-     */
-    public static String formatPrice(BigDecimal price) {
-        if (price == null) {
+    public static String formatNumber(BigDecimal number, String formatType, Delegator delegator, Locale locale) {
+        if (number == null) {
             return "";
         }
-        return priceDecimalFormat.format(price);
+        return formatNumber(number.doubleValue(), formatType, delegator, locale);
     }
 
-    /** Formats a double representing a price into a string
-     * @param price The price double to be formatted
+    /** Formats a Double representing a price into a string
+     * @param price The price Double to be formatted
      * @return A String with the formatted price
      */
-    public static String formatPrice(double price) {
-        return priceDecimalFormat.format(price);
+    @Deprecated
+    public static String formatPrice(Double price) {
+        return formatNumber(price, AMOUNT_FORMAT, null, null);
     }
 
-    public static Double formatPriceNumber(double price) {
-        try {
-            return priceDecimalFormat.parse(formatPrice(price)).doubleValue();
-        } catch (ParseException e) {
-            Debug.logError(e, module);
-            return price;
-        }
+    /** Formats a BigDecimal representing a price into a string
+     * @param price The price BigDecimal to be formatted
+     * @return A String with the formatted price
+     */
+    @Deprecated
+    public static String formatPrice(BigDecimal price) {
+        return formatNumber(price, AMOUNT_FORMAT, null, null);
     }
 
     /** Formats a double into a properly formatted currency string based on isoCode and Locale
@@ -157,14 +179,6 @@ public final class UtilFormatOut {
      * @return A String with the formatted number
      */
     public static String formatSpelledOutAmount(Double amount, Locale locale) {
-        return formatSpelledOutAmount(amount.doubleValue(), locale);
-    }
-    /** Formats a double into a properly spelled out number string based on Locale
-     * @param amount The amount double to be formatted
-     * @param locale The Locale used to format the number
-     * @return A String with the formatted number
-     */
-    public static String formatSpelledOutAmount(double amount, Locale locale) {
         com.ibm.icu.text.NumberFormat nf = new com.ibm.icu.text.RuleBasedNumberFormat(locale, com.ibm.icu.text.RuleBasedNumberFormat.SPELLOUT);
         return nf.format(amount);
     }
@@ -176,10 +190,7 @@ public final class UtilFormatOut {
      */
     // This method should be used in place of formatPrice because it is locale aware.
     public static String formatAmount(double amount, Locale locale) {
-        com.ibm.icu.text.NumberFormat nf = com.ibm.icu.text.NumberFormat.getInstance(locale);
-        nf.setMinimumFractionDigits(2);
-        nf.setMaximumFractionDigits(2);
-        return nf.format(amount);
+        return formatNumber(amount, AMOUNT_FORMAT, null, locale);
     }
 
     /** Formats a Double representing a percentage into a string
@@ -187,10 +198,7 @@ public final class UtilFormatOut {
      * @return A String with the formatted percentage
      */
     public static String formatPercentage(Double percentage) {
-        if (percentage == null) {
-            return "";
-        }
-        return formatPercentage(percentage.doubleValue());
+        return formatNumber(percentage, PERCENTAGE_FORMAT, null, null);
     }
 
     /** Formats a BigDecimal representing a percentage into a string
@@ -198,30 +206,20 @@ public final class UtilFormatOut {
      * @return A String with the formatted percentage
      */
     public static String formatPercentage(BigDecimal percentage) {
-        if (percentage == null) {
-            return "";
-        }
-        return percentageDecimalFormat.format(percentage);
-    }
+        return formatNumber(percentage, PERCENTAGE_FORMAT, null, null);
 
-    /** Formats a double representing a percentage into a string
-     * @param percentage The percentage double to be formatted
-     * @return A String with the formatted percentage
-     */
-    public static String formatPercentage(double percentage) {
-        return percentageDecimalFormat.format(percentage);
     }
 
     /** Formats a BigDecimal value 1:1 into a percentage string (e.g. 10 to 10% instead of 0,1 to 10%)
      * @param percentage The percentage Decimal to be formatted
      * @return A String with the formatted percentage
      */
-    public static String formatPercentageRate(BigDecimal percentage, boolean negate) {
-        if (percentage == null) return "";
-        if (negate) {
-            return percentageDecimalFormat.format(percentage.divide(BigDecimal.valueOf(-100)));
+        public static String formatPercentageRate(BigDecimal percentage, boolean negate) {
+        if (percentage == null) {
+            return "";
         }
-        return percentageDecimalFormat.format(percentage.divide(BigDecimal.valueOf(100)));
+        BigDecimal hundred = BigDecimal.valueOf(negate? -100: 100);
+        return formatNumber(percentage.divide(hundred), PERCENTAGE_FORMAT, null, null);
     }
 
     /** Formats an Long representing a quantity into a string
@@ -235,14 +233,6 @@ public final class UtilFormatOut {
         return formatQuantity(quantity.doubleValue());
     }
 
-    /** Formats an int representing a quantity into a string
-     * @param quantity The quantity long to be formatted
-     * @return A String with the formatted quantity
-     */
-    public static String formatQuantity(long quantity) {
-        return formatQuantity((double) quantity);
-    }
-
     /** Formats an Integer representing a quantity into a string
      * @param quantity The quantity Integer to be formatted
      * @return A String with the formatted quantity
@@ -254,14 +244,6 @@ public final class UtilFormatOut {
         return formatQuantity(quantity.doubleValue());
     }
 
-    /** Formats an int representing a quantity into a string
-     * @param quantity The quantity int to be formatted
-     * @return A String with the formatted quantity
-     */
-    public static String formatQuantity(int quantity) {
-        return formatQuantity((double) quantity);
-    }
-
     /** Formats a Float representing a quantity into a string
      * @param quantity The quantity Float to be formatted
      * @return A String with the formatted quantity
@@ -273,23 +255,12 @@ public final class UtilFormatOut {
         return formatQuantity(quantity.doubleValue());
     }
 
-    /** Formats a float representing a quantity into a string
-     * @param quantity The quantity float to be formatted
-     * @return A String with the formatted quantity
-     */
-    public static String formatQuantity(float quantity) {
-        return formatQuantity((double) quantity);
-    }
-
     /** Formats an Double representing a quantity into a string
      * @param quantity The quantity Double to be formatted
      * @return A String with the formatted quantity
      */
     public static String formatQuantity(Double quantity) {
-        if (quantity == null) {
-            return "";
-        }
-        return formatQuantity(quantity.doubleValue());
+        return formatNumber(quantity, QUANTITY_FORMAT, null, null);
     }
 
     /** Formats an BigDecimal representing a quantity into a string
@@ -297,18 +268,7 @@ public final class UtilFormatOut {
      * @return A String with the formatted quantity
      */
     public static String formatQuantity(BigDecimal quantity) {
-        if (quantity == null) {
-            return "";
-        }
-        return quantityDecimalFormat.format(quantity);
-    }
-
-    /** Formats an double representing a quantity into a string
-     * @param quantity The quantity double to be formatted
-     * @return A String with the formatted quantity
-     */
-    public static String formatQuantity(double quantity) {
-        return quantityDecimalFormat.format(quantity);
+        return formatNumber(quantity, QUANTITY_FORMAT, null, null);
     }
 
     public static String formatPaddedNumber(long number, int numericPadding) {

Modified: ofbiz/ofbiz-framework/trunk/framework/common/config/general.properties
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/common/config/general.properties?rev=1864832&r1=1864831&r2=1864832&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/common/config/general.properties (original)
+++ ofbiz/ofbiz-framework/trunk/framework/common/config/general.properties Fri Aug  9 20:37:11 2019
@@ -29,18 +29,6 @@ ORGANIZATION_PARTY=Company
 # ID of the VisualTheme to use if there is no VISUAL_THEME UserPreference record for the current user (ie default value)
 VISUAL_THEME=RAINBOWSTONE_SAPHIR
 
-# -- the default decimal format for currency (used in UtilFormatOut.java)
-currency.decimal.format=#,##0.00
-
-# -- the default rounding for currency (used in OfbizCurrencyTransform.java)
-currency.rounding.default=10
-
-# -- the default check scale for integer currency enabled (Y|N) (used in OfbizCurrencyTransform.java)
-#When decimals are '00'
-# -- Y if you want to display only x , example : 10
-# -- N if you want to display x.00 , example : 10.00
-currency.scale.enabled=N
-
 # -- Properties fallback locale. Change this setting with caution. If you
 #    start getting "resource not found" exceptions, then there are
 #    properties missing in the locale you specified. This property does not

Added: ofbiz/ofbiz-framework/trunk/framework/common/config/number.properties
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/common/config/number.properties?rev=1864832&view=auto
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/common/config/number.properties (added)
+++ ofbiz/ofbiz-framework/trunk/framework/common/config/number.properties Fri Aug  9 20:37:11 2019
@@ -0,0 +1,32 @@
+
+# Define format number use on OFBiz to display it
+# You can surchage by entity SystemProperty
+# for the templating format, please see javadoc
+
+# Default displaying template
+default.displaying.format = ##0.00
+
+# Displaying number on accounting context
+accounting.displaying.format = #,##0.0000;(#,##0.0000)
+
+# Displaying a quantity on general case. for specific you need to define you own purpose
+quantity.displaying.format = ##0.0000000
+
+#Displaying an amount on general case. This is different than the displaying currency number.
+amount.displaying.format = #0.000
+
+#Displaying a number in percentage
+percentage.displaying.format = ##.##%
+
+
+# Specific properties for currency
+# --------------------------------
+
+# -- the default rounding for currency (used in OfbizCurrencyTransform.java)
+currency.rounding.default=10
+
+# -- the default check scale for integer currency enabled (Y|N) (used in OfbizCurrencyTransform.java)
+#When decimals are '00'
+# -- Y if you want to display only x , example : 10
+# -- N if you want to display x.00 , example : 10.00
+currency.scale.enabled=N
\ No newline at end of file

Propchange: ofbiz/ofbiz-framework/trunk/framework/common/config/number.properties
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/ofbiz-framework/trunk/framework/common/config/number.properties
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/ofbiz-framework/trunk/framework/common/config/number.properties
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: ofbiz/ofbiz-framework/trunk/framework/webapp/config/freemarkerTransforms.properties
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/webapp/config/freemarkerTransforms.properties?rev=1864832&r1=1864831&r2=1864832&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/webapp/config/freemarkerTransforms.properties (original)
+++ ofbiz/ofbiz-framework/trunk/framework/webapp/config/freemarkerTransforms.properties Fri Aug  9 20:37:11 2019
@@ -25,6 +25,7 @@ ofbizUrl=org.apache.ofbiz.webapp.ftl.Ofb
 ofbizContentUrl=org.apache.ofbiz.webapp.ftl.OfbizContentTransform
 ofbizCurrency=org.apache.ofbiz.webapp.ftl.OfbizCurrencyTransform
 ofbizAmount=org.apache.ofbiz.webapp.ftl.OfbizAmountTransform
+ofbizNumber=org.apache.ofbiz.webapp.ftl.OfbizNumberTransform
 setRequestAttribute=org.apache.ofbiz.webapp.ftl.SetRequestAttributeMethod
 renderWrappedText=org.apache.ofbiz.webapp.ftl.RenderWrappedTextTransform
 setContextField=org.apache.ofbiz.webapp.ftl.SetContextFieldTransform

Modified: ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizAmountTransform.java
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizAmountTransform.java?rev=1864832&r1=1864831&r2=1864832&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizAmountTransform.java (original)
+++ ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizAmountTransform.java Fri Aug  9 20:37:11 2019
@@ -18,127 +18,26 @@
  *******************************************************************************/
 package org.apache.ofbiz.webapp.ftl;
 
-import java.io.IOException;
 import java.io.Writer;
-import java.util.Locale;
 import java.util.Map;
-
-import javax.servlet.http.HttpServletRequest;
-
-import org.apache.ofbiz.base.util.Debug;
 import org.apache.ofbiz.base.util.UtilFormatOut;
 import org.apache.ofbiz.base.util.UtilGenerics;
-import org.apache.ofbiz.base.util.UtilHttp;
-
-import freemarker.core.Environment;
-import freemarker.ext.beans.BeanModel;
-import freemarker.ext.beans.NumberModel;
-import freemarker.template.SimpleNumber;
-import freemarker.template.SimpleScalar;
-import freemarker.template.TemplateModelException;
-import freemarker.template.TemplateScalarModel;
-import freemarker.template.TemplateTransformModel;
 
 /**
  * OfbizAmountTransform - Freemarker Transform for content links
+ * This class is keep for backware compatibilty and call directly
+ * OfbizNumberTransform with good arguments :
+ *    * amout translate to number
+ *    * format force to UtilFormatOut.AMOUNT_FORMAT
  */
-public class OfbizAmountTransform implements TemplateTransformModel {
+public class OfbizAmountTransform extends OfbizNumberTransform {
 
     public static final String module = OfbizAmountTransform.class.getName();
-    public static final String SPELLED_OUT_FORMAT = "spelled-out";
-
-    private static String getArg(Map<String, Object> args, String key) {
-        String  result = "";
-        Object o = args.get(key);
-        if (o != null) {
-            if (Debug.verboseOn()) Debug.logVerbose("Arg Object : " + o.getClass().getName(), module);
-            if (o instanceof TemplateScalarModel) {
-                TemplateScalarModel s = (TemplateScalarModel) o;
-                try {
-                    result = s.getAsString();
-                } catch (TemplateModelException e) {
-                    Debug.logError(e, "Template Exception", module);
-                }
-            } else {
-              result = o.toString();
-            }
-        }
-        return result;
-    }
-    private static Double getAmount(Map<String, Object> args, String key) {
-        if (args.containsKey(key)) {
-            Object o = args.get(key);
-            if (Debug.verboseOn()) Debug.logVerbose("Amount Object : " + o.getClass().getName(), module);
-
-            // handle nulls better
-            if (o == null) {
-                o = 0.00;
-            }
 
-            if (o instanceof NumberModel) {
-                NumberModel s = (NumberModel) o;
-                return s.getAsNumber().doubleValue();
-            }
-            if (o instanceof SimpleNumber) {
-                SimpleNumber s = (SimpleNumber) o;
-                return s.getAsNumber().doubleValue();
-            }
-            if (o instanceof SimpleScalar) {
-                SimpleScalar s = (SimpleScalar) o;
-                return Double.valueOf(s.getAsString());
-            }
-            return Double.valueOf(o.toString());
-        }
-        return 0.00;
-    }
-
-    @Override
     public Writer getWriter(Writer out, @SuppressWarnings("rawtypes") Map args) {
-        final StringBuilder buf = new StringBuilder();
-
         Map<String, Object> arguments = UtilGenerics.cast(args);
-        final Double amount = OfbizAmountTransform.getAmount(arguments, "amount");
-        final String locale = OfbizAmountTransform.getArg(arguments, "locale");
-        final String format = OfbizAmountTransform.getArg(arguments, "format");
-
-        return new Writer(out) {
-            @Override
-            public void write(char cbuf[], int off, int len) {
-                buf.append(cbuf, off, len);
-            }
-
-            @Override
-            public void flush() throws IOException {
-                out.flush();
-            }
-
-            @Override
-            public void close() throws IOException {
-                try {
-                    if (Debug.verboseOn()) Debug.logVerbose("parms: " + amount + " " + format + " " + locale, module);
-                    Locale localeObj = null;
-                    if (locale.length() < 1) {
-                        // Load the locale from the session
-                        Environment env = Environment.getCurrentEnvironment();
-                        BeanModel req = (BeanModel) env.getVariable("request");
-                        if (req != null) {
-                            HttpServletRequest request = (HttpServletRequest) req.getWrappedObject();
-                            localeObj = UtilHttp.getLocale(request);
-                        } else {
-                            localeObj = env.getLocale();
-                        }
-                    } else {
-                        localeObj = new Locale(locale);
-                    }
-                    if (format.equals(OfbizAmountTransform.SPELLED_OUT_FORMAT)) {
-                        out.write(UtilFormatOut.formatSpelledOutAmount(amount.doubleValue(), localeObj));
-                    } else {
-                        out.write(UtilFormatOut.formatAmount(amount, localeObj));
-                    }
-                } catch (TemplateModelException e) {
-                    throw new IOException(e.getMessage());
-                }
-            }
-        };
+        arguments.put("format", UtilFormatOut.AMOUNT_FORMAT);
+        arguments.put("number", args.get("amount"));
+        return super.getWriter(out, arguments);
     }
 }

Modified: ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizCurrencyTransform.java
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizCurrencyTransform.java?rev=1864832&r1=1864831&r2=1864832&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizCurrencyTransform.java (original)
+++ ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizCurrencyTransform.java Fri Aug  9 20:37:11 2019
@@ -141,9 +141,9 @@ public class OfbizCurrencyTransform impl
             Delegator delegator = (Delegator) request.getAttribute("delegator");
             // Get rounding from SystemProperty
             if (UtilValidate.isNotEmpty(delegator)) {
-                scaleEnabled = EntityUtilProperties.getPropertyValue("general", "currency.scale.enabled", "N", delegator);
+                scaleEnabled = EntityUtilProperties.getPropertyValue("number", "currency.scale.enabled", "N", delegator);
                 if (UtilValidate.isEmpty(roundingNumber)) {
-                    String roundingString = EntityUtilProperties.getPropertyValue("general", "currency.rounding.default", "10", delegator);
+                    String roundingString = EntityUtilProperties.getPropertyValue("number", "currency.rounding.default", "10", delegator);
                     if (UtilValidate.isInteger(roundingString)) roundingNumber = Integer.parseInt(roundingString);
                 }
             }

Added: ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizNumberTransform.java
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizNumberTransform.java?rev=1864832&view=auto
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizNumberTransform.java (added)
+++ ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizNumberTransform.java Fri Aug  9 20:37:11 2019
@@ -0,0 +1,140 @@
+/*******************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.ofbiz.webapp.ftl;
+
+import freemarker.core.Environment;
+import freemarker.ext.beans.BeanModel;
+import freemarker.ext.beans.NumberModel;
+import freemarker.template.SimpleNumber;
+import freemarker.template.SimpleScalar;
+import freemarker.template.TemplateModelException;
+import freemarker.template.TemplateScalarModel;
+import freemarker.template.TemplateTransformModel;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Locale;
+import java.util.Map;
+import javax.servlet.http.HttpServletRequest;
+import org.apache.ofbiz.base.util.Debug;
+import org.apache.ofbiz.base.util.UtilFormatOut;
+import org.apache.ofbiz.base.util.UtilGenerics;
+import org.apache.ofbiz.base.util.UtilHttp;
+import org.apache.ofbiz.entity.Delegator;
+
+/**
+ * OfbizAmountTransform - Freemarker Transform for content links
+ */
+public class OfbizNumberTransform implements TemplateTransformModel {
+
+    public static final String module = OfbizNumberTransform.class.getName();
+    String format = null;
+
+    private static String getArg(Map<String, Object> args, String key) {
+        String  result = "";
+        Object o = args.get(key);
+        if (o != null) {
+            if (Debug.verboseOn()) Debug.logVerbose("Arg Object : " + o.getClass().getName(), module);
+            if (o instanceof TemplateScalarModel) {
+                TemplateScalarModel s = (TemplateScalarModel) o;
+                try {
+                    result = s.getAsString();
+                } catch (TemplateModelException e) {
+                    Debug.logError(e, "Template Exception", module);
+                }
+            } else {
+              result = o.toString();
+            }
+        }
+        return result;
+    }
+    private static Double getNumber(Map<String, Object> args, String key) {
+        if (args.containsKey(key)) {
+            Object o = args.get(key);
+            if (Debug.verboseOn()) Debug.logVerbose("Number Object : " + o.getClass().getName(), module);
+
+            // handle nulls better
+            if (o == null) {
+                o = 0.00;
+            }
+
+            if (o instanceof NumberModel) {
+                NumberModel s = (NumberModel) o;
+                return s.getAsNumber().doubleValue();
+            }
+            if (o instanceof SimpleNumber) {
+                SimpleNumber s = (SimpleNumber) o;
+                return s.getAsNumber().doubleValue();
+            }
+            if (o instanceof SimpleScalar) {
+                SimpleScalar s = (SimpleScalar) o;
+                return Double.valueOf(s.getAsString());
+            }
+            return Double.valueOf(o.toString());
+        }
+        return 0.00;
+    }
+
+    @Override
+    public Writer getWriter(Writer out, @SuppressWarnings("rawtypes") Map args) {
+        final StringBuilder buf = new StringBuilder();
+
+        Map<String, Object> arguments = UtilGenerics.cast(args);
+        final Double number = OfbizNumberTransform.getNumber(arguments, "number");
+        final String locale = OfbizNumberTransform.getArg(arguments, "locale");
+        final String format = OfbizNumberTransform.getArg(arguments, "format");
+
+        return new Writer(out) {
+            @Override
+            public void write(char cbuf[], int off, int len) {
+                buf.append(cbuf, off, len);
+            }
+
+            @Override
+            public void flush() throws IOException {
+                out.flush();
+            }
+
+            @Override
+            public void close() throws IOException {
+                try {
+                    if (Debug.verboseOn()) Debug.logVerbose("parms: " + number + " " + format + " " + locale, module);
+                    Locale localeObj = null;
+                    Delegator delegator = null;
+                    // Load the locale from the session
+                    Environment env = Environment.getCurrentEnvironment();
+                    BeanModel req = (BeanModel) env.getVariable("request");
+                    if (req != null) {
+                        HttpServletRequest request = (HttpServletRequest) req.getWrappedObject();
+                        delegator = (Delegator) request.getAttribute("delegator");
+                        if (locale.length() < 1) {
+                            localeObj = UtilHttp.getLocale(request);
+                        } else {
+                            localeObj = env.getLocale();
+                        }
+                    } else {
+                        localeObj = new Locale(locale);
+                    }
+                    out.write(UtilFormatOut.formatNumber(number, format, delegator, localeObj));
+                } catch (TemplateModelException e) {
+                    throw new IOException(e.getMessage());
+                }
+            }
+        };
+    }
+}

Propchange: ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizNumberTransform.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizNumberTransform.java
------------------------------------------------------------------------------
    svn:keywords = Date Rev Author URL Id

Propchange: ofbiz/ofbiz-framework/trunk/framework/webapp/src/main/java/org/apache/ofbiz/webapp/ftl/OfbizNumberTransform.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: ofbiz/ofbiz-framework/trunk/framework/webtools/widget/MiscForms.xml
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/webtools/widget/MiscForms.xml?rev=1864832&r1=1864831&r2=1864832&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/webtools/widget/MiscForms.xml (original)
+++ ofbiz/ofbiz-framework/trunk/framework/webtools/widget/MiscForms.xml Fri Aug  9 20:37:11 2019
@@ -28,10 +28,19 @@ under the License.
     </form>
 
     <form name="LayoutDemoForm" type="single" target="${demoTargetUrl}" default-map-name="demoMap">
+        <actions>
+            <set field="numberValue" value="-9.958"/>
+        </actions>
         <field name="name" title="${uiLabelMap.CommonName}" required-field="true">
             <text />
         </field>
         <field name="description" title="${uiLabelMap.CommonDescription}"><text/></field>
+        <field name="default"><display type="number" description="${numberValue}"/></field>
+        <field name="quantity"><display type="number" format="quantity" description="${numberValue}"/></field>
+        <field name="amount"><display type="number" format="amount" description="${numberValue}"/></field>
+        <field name="spelled"><display type="number" format="spelled-out" description="${numberValue}"/></field>
+        <field name="percentage"><display type="number" format="percentage" description="${numberValue}"/></field>
+        <field name="accounting"><display type="number" format="accounting" description="${numberValue}"/></field>
         <field name="dropDown" title="${uiLabelMap.CommonEnabled}">
             <drop-down>
                 <option key="Y" description="${uiLabelMap.CommonYes}" />

Modified: ofbiz/ofbiz-framework/trunk/framework/widget/dtd/widget-form.xsd
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/widget/dtd/widget-form.xsd?rev=1864832&r1=1864831&r2=1864832&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/widget/dtd/widget-form.xsd (original)
+++ ofbiz/ofbiz-framework/trunk/framework/widget/dtd/widget-form.xsd Fri Aug  9 20:37:11 2019
@@ -990,6 +990,11 @@ under the License.
                                 <xs:documentation>Display negatives in parentheses (configurable, see arithmetic.properties)</xs:documentation>
                             </xs:annotation>
                         </xs:enumeration>
+                        <xs:enumeration value="number">
+                            <xs:annotation>
+                                <xs:documentation>Display as number with the format type given (configurable, see arithmetic.properties)</xs:documentation>
+                            </xs:annotation>
+                        </xs:enumeration>
                     </xs:restriction>
                 </xs:simpleType>
             </xs:attribute>
@@ -1003,6 +1008,13 @@ under the License.
                     <xs:documentation>Specifies the image to display.</xs:documentation>
                 </xs:annotation>
             </xs:attribute>
+            <xs:attribute type="xs:string" name="format">
+                <xs:annotation>
+                    <xs:documentation>Format a type number, select the format type to use on arithmetic.properties with the pattern ${format}.displaying.format =
+                        ex: default is present in number.properties as 'default.displaying.format = ##0.00'
+                    </xs:documentation>
+                </xs:annotation>
+            </xs:attribute>
             <xs:attribute type="xs:string" name="default-value">
                 <xs:annotation>
                     <xs:documentation>Specifies a string to be displayed if the field is empty.</xs:documentation>

Modified: ofbiz/ofbiz-framework/trunk/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelFormField.java
URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelFormField.java?rev=1864832&r1=1864831&r2=1864832&view=diff
==============================================================================
--- ofbiz/ofbiz-framework/trunk/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelFormField.java (original)
+++ ofbiz/ofbiz-framework/trunk/framework/widget/src/main/java/org/apache/ofbiz/widget/model/ModelFormField.java Fri Aug  9 20:37:11 2019
@@ -66,6 +66,7 @@ import org.apache.ofbiz.entity.finder.En
 import org.apache.ofbiz.entity.model.ModelEntity;
 import org.apache.ofbiz.entity.model.ModelUtil;
 import org.apache.ofbiz.entity.util.EntityUtil;
+import org.apache.ofbiz.entity.util.EntityUtilProperties;
 import org.apache.ofbiz.widget.WidgetWorker;
 import org.apache.ofbiz.widget.model.CommonWidgetModels.AutoEntityParameters;
 import org.apache.ofbiz.widget.model.CommonWidgetModels.AutoServiceParameters;
@@ -1485,6 +1486,7 @@ public class ModelFormField {
         private final FlexibleStringExpander defaultValue;
         private final FlexibleStringExpander description;
         private final FlexibleStringExpander imageLocation;
+        private final FlexibleStringExpander format;
         private final InPlaceEditor inPlaceEditor;
         private final String size; // maximum number of characters to display
         private final String type; // matches type of field, currently text or currency
@@ -1498,6 +1500,7 @@ public class ModelFormField {
             this.description = original.description;
             this.imageLocation = original.imageLocation;
             this.inPlaceEditor = original.inPlaceEditor;
+            this.format = original.format;
             this.size = original.size;
             this.type = original.type;
         }
@@ -1510,6 +1513,7 @@ public class ModelFormField {
             this.defaultValue = FlexibleStringExpander.getInstance(element.getAttribute("default-value"));
             this.description = FlexibleStringExpander.getInstance(element.getAttribute("description"));
             this.imageLocation = FlexibleStringExpander.getInstance(element.getAttribute("image-location"));
+            this.format = FlexibleStringExpander.getInstance(element.getAttribute("format"));
             Element inPlaceEditorElement = UtilXml.firstChildElement(element, "in-place-editor");
             if (inPlaceEditorElement != null) {
                 this.inPlaceEditor = new InPlaceEditor(inPlaceEditorElement);
@@ -1528,6 +1532,7 @@ public class ModelFormField {
             this.defaultValue = FlexibleStringExpander.getInstance("");
             this.description = FlexibleStringExpander.getInstance("");
             this.imageLocation = FlexibleStringExpander.getInstance("");
+            this.format = FlexibleStringExpander.getInstance("");
             this.inPlaceEditor = null;
             this.size = "";
             this.type = "";
@@ -1541,6 +1546,7 @@ public class ModelFormField {
             this.defaultValue = FlexibleStringExpander.getInstance("");
             this.description = FlexibleStringExpander.getInstance("");
             this.imageLocation = FlexibleStringExpander.getInstance("");
+            this.format = FlexibleStringExpander.getInstance("");
             this.inPlaceEditor = null;
             this.size = "";
             this.type = "";
@@ -1554,6 +1560,7 @@ public class ModelFormField {
             this.defaultValue = FlexibleStringExpander.getInstance("");
             this.description = FlexibleStringExpander.getInstance("");
             this.imageLocation = FlexibleStringExpander.getInstance("");
+            this.format = FlexibleStringExpander.getInstance("");
             this.inPlaceEditor = null;
             this.size = "";
             this.type = "";
@@ -1671,16 +1678,24 @@ public class ModelFormField {
                     // create default date/time value from timestamp string
                     retVal = retVal.substring(0, 16);
                 }
-            } else if ("accounting-number".equals(this.type)) {
+            } else if ("number".equals(this.type) ||
+                    (this.type != null && this.type.endsWith("-number"))) {
                 Locale locale = (Locale) context.get("locale");
                 if (locale == null) {
                     locale = Locale.getDefault();
                 }
+                String formatVal;
+                if (! this.format.isEmpty()) {
+                    formatVal = this.format.expandString(context);
+                } else {
+                    formatVal = this.type.endsWith("-number")?
+                        this.type.replaceFirst("-number", "")
+                        :"default";
+                }
+                Delegator delegator = (Delegator) context.get("delegator");
                 try {
                     Double parsedRetVal = (Double) ObjectType.simpleTypeOrObjectConvert(retVal, "Double", null, locale, false);
-                    String template = UtilProperties.getPropertyValue("arithmetic", "accounting-number.format",
-                            "#,##0.00;(#,##0.00)");
-                    retVal = UtilFormatOut.formatDecimalNumber(parsedRetVal, template, locale);
+                    retVal = UtilFormatOut.formatNumber(parsedRetVal, formatVal, delegator, locale);
                 } catch (GeneralException e) {
                     String errMsg = "Error formatting number [" + retVal + "]: " + e.toString();
                     Debug.logError(e, errMsg, module);



Re: svn commit: r1864832 - in /ofbiz/ofbiz-framework/trunk: applications/accounting/config/ applications/order/template/order/ applications/order/widget/ordermgr/ framework/base/src/main/java/org/apache/ofbiz/base/util/ framework/common/config/ framework/w...

Posted by Mathieu Lirzin <ma...@nereide.fr>.
Nicolas Malin <ni...@nereide.fr> writes:

> On 8/10/19 12:07 AM, Mathieu Lirzin wrote:
>
>> Here are a few inline comments regarding the code.
>>
>> Maybe I overlooked some good reason justifying some design decision you
>> made, so if you want to discuss more about the suggestions I proposed,
>> we can do some pair programming next week.
>
> It was a big task, so I focused my mind only on how homogenize and
> centralize a number displaying. So if you I have some idea to continue
> the improvement through other design concept, It's welcome :)

This is indeed a big effort which is adding a great amount of
flexibility.

>>>        */
>>> -    public static String formatPrice(Double price) {
>>> -        if (price == null) {
>>> +    public static String formatNumber(Double number, String formatType, Delegator delegator, Locale locale) {
>> The dependency on the Delegator should be avoided if possible because it
>> makes writing unit complex tests. By the way where are those tests ? :-)
> Normally not because delegator and locale are optional. If it's not
> the case, I will improve it to support unit test

To me this is the only critical part that needs to be fixed because
previous implementation was achieving better testability. Other remarks
are less important improvements that can be worked on later.

>> To remove a dependency on a class which is hard to instantiate like
>> ‘Delegator’, the common solution is to replace it with an interface
>> (ideally a functional interface to let the caller pass a lambda).
>>
>> In this particular case after a quick look, the ‘delegator’ argument
>> could potentially be replaced by a ‘templateProvider’ argument of type
>> ‘Function<String, String>’ where the input is a format type and the
>> output is a template.
>>
>> When things are complicated to decouple, an alternative is write an
>> overload specifically for the test and use it inside the coupled one.
> I's seem to be a good idea :) but I'm far away to understand what do
> you explain, I need to studies more ^^

Decoupling methods from stateful objects like ‘Delegator’,
‘HttpServletrequest’ and ‘GenericValue’ is hard to explain and
understand theorically, however in practice when adopting the hygiene of
writing unit tests with plain java objects it become second nature. :-)

Thanks.

-- 
Mathieu Lirzin
GPG: F2A3 8D7E EB2B 6640 5761  070D 0ADE E100 9460 4D37

Re: svn commit: r1864832 - in /ofbiz/ofbiz-framework/trunk: applications/accounting/config/ applications/order/template/order/ applications/order/widget/ordermgr/ framework/base/src/main/java/org/apache/ofbiz/base/util/ framework/common/config/ framework/w...

Posted by Nicolas Malin <ni...@nereide.fr>.
On 8/10/19 12:07 AM, Mathieu Lirzin wrote:
> Hello Nicolas,

Hi man,

> Here are a few inline comments regarding the code.
>
> Maybe I overlooked some good reason justifying some design decision you
> made, so if you want to discuss more about the suggestions I proposed,
> we can do some pair programming next week.

It was a big task, so I focused my mind only on how homogenize and 
centralize a number displaying. So if you I have some idea to continue 
the improvement through other design concept, It's welcome :)

> [...]
>
>> @@ -34,16 +34,11 @@ import java.util.TimeZone;
>>   public final class UtilFormatOut {
>>   
>>       public static final String module = UtilFormatOut.class.getName();
>> -
>> -    // ------------------- price format handlers -------------------
>> -    // FIXME: This is not thread-safe! DecimalFormat is not synchronized.
>> -    private static final DecimalFormat priceDecimalFormat = new DecimalFormat(UtilProperties.getPropertyValue("general", "currency.decimal.format", "#,##0.00"));
>> -
>> -    // ------------------- quantity format handlers -------------------
>> -    private static final DecimalFormat quantityDecimalFormat = new DecimalFormat("#,##0.###");
>> -
>> -    // ------------------- percentage format handlers -------------------
>> -    private static final DecimalFormat percentageDecimalFormat = new DecimalFormat("##0.##%");
>> +    public static final String DEFAULT_FORMAT = "default";
>> +    public static final String AMOUNT_FORMAT = "amount";
>> +    public static final String QUANTITY_FORMAT = "quantity";
>> +    public static final String PERCENTAGE_FORMAT = "percentage";
>> +    public static final String SPELLED_OUT_FORMAT = "spelled-out";
> Intuitively an ‘enum’ would seem more appropriate to make the relation
> between those constants clearer.
>
> --8<---------------cut here---------------start------------->8---
> enum Format {
>     DEFAULT, AMOUNT, QUANTITY, PERCENTAGE, SPELLED_OUT;
>
>     // Converts a string to a typed value.
>     static Format valueOf(String name) {
>        ...
>     }
> }
> --8<---------------cut here---------------end--------------->8---
>
> Additionally I would be nice if the meaning of those “symbols” could be
> documented inside the code either in the XSD with a reference in the
> Java code, or in both in XSD and Java.
Indeed
>       private UtilFormatOut() {}
>   
> @@ -54,43 +49,70 @@ public final class UtilFormatOut {
>           return "";
>       }
>   
> -    /** Formats a Double representing a price into a string
> -     * @param price The price Double to be formatted
> -     * @return A String with the formatted price
> +    /** Format a number with format type define by properties
> +     *
> Please expand the javadoc instead of removing it.
I will check, maybe it's an error
>>        */
>> -    public static String formatPrice(Double price) {
>> -        if (price == null) {
>> +    public static String formatNumber(Double number, String formatType, Delegator delegator, Locale locale) {
> The dependency on the Delegator should be avoided if possible because it
> makes writing unit complex tests. By the way where are those tests ? :-)
Normally not because delegator and locale are optional. If it's not the 
case, I will improve it to support unit test
>
> To remove a dependency on a class which is hard to instantiate like
> ‘Delegator’, the common solution is to replace it with an interface
> (ideally a functional interface to let the caller pass a lambda).
>
> In this particular case after a quick look, the ‘delegator’ argument
> could potentially be replaced by a ‘templateProvider’ argument of type
> ‘Function<String, String>’ where the input is a format type and the
> output is a template.
>
> When things are complicated to decouple, an alternative is write an
> overload specifically for the test and use it inside the coupled one.
I's seem to be a good idea :) but I'm far away to understand what do you 
explain, I need to studies more ^^
>> +        if (number == null) {
>>               return "";
>>           }
>> -        return formatPrice(price.doubleValue());
>> +        if (formatType == null) {
>> +            formatType = DEFAULT_FORMAT;
>> +        }
>> +        if (locale == null) {
>> +            locale = Locale.getDefault();
>> +        }
> Does silencing ‘null’ values really make sense here? if ‘null’ values
> doesn't have sound meaning, I would recommend bailing out early instead
> of silencing a possible programming mistake or worse let another method
> downstream throw a cryptic exception later.  To bail out early you can
> use things like ‘Objects.requireNonNull(myArg)’.
I use the silencing because we are on displaying number so instead 
deploy complex analyze with risk to break something during the 
rendering, I prefer to use safe call en just return empty String.
> Have a nice weekend!

Thanks for your time and remarks

Nicolas

>

Re: svn commit: r1864832 - in /ofbiz/ofbiz-framework/trunk: applications/accounting/config/ applications/order/template/order/ applications/order/widget/ordermgr/ framework/base/src/main/java/org/apache/ofbiz/base/util/ framework/common/config/ framework/w...

Posted by Mathieu Lirzin <ma...@nereide.fr>.
Hello Nicolas,

Here are a few inline comments regarding the code.

Maybe I overlooked some good reason justifying some design decision you
made, so if you want to discuss more about the suggestions I proposed,
we can do some pair programming next week.

nmalin@apache.org writes:

> Author: nmalin
> Date: Fri Aug  9 20:37:11 2019
> New Revision: 1864832
>
> URL: http://svn.apache.org/viewvc?rev=1864832&view=rev
> Log:
> Implemented: Homogenize displaying number with multiple format
> (OFBIZ-7532)
>
> To display a number we had different possibilities :
>  * on ftl use the template <@ofbizAmount and <@ofbizCurrency
>  * by java call a function UtilFormatOut.formatAmount, UtilFormatOut.formatPrice, UtilFormatOut.formatQuantity, etc..
>  * by form widget, use <display type=accounting-number for accounting but nothing for other
>
> To simplify and homogenize all, I implemented a number type purpose :
>    * default: display a number by default, use when no purpose is present
>    * quantity: display a number as a quantity
>    * amount: display a number as an amount (like price without currency)
>    * spelled: litteral displaying for a number (use on <@ofbizAmount ftl only before)
>    * percentage: display a number as a percentage
>    * accounting: diplay a number for accounting specific
>
> Each purpose can be associate to a number for displaying it :
>    * on ftl <@ofbizNumber number=value format=purpose/>
>    * on java UtilFormatOut.formatNumber(value, purpose, delegator, locale)
>    * on form widget <display type=number format=purpose/>
>
> The format use by a purpose is define on framework/common/config/number.properties with the template
>     .displaying.format = ##0.00
>
> With this, you can surchage a configuration, create your own purpose or surchage only one through entity SystemProperty.
>
> Concerning the backware compatibility: 
>  * For the ftl the template <@ofbizAmount is now a link to '<@ofbizNumber format=amount'
>  * For java all previous function call UtilFormatOut.formatNumber with the matching purpose
>  * For form xml accounting-number is managed as an exection
>
> Last point, display a currency is different that a number, so I didn't refactoring some code for this case (only move properties from general to number for centralize de configuration on the same file)
>
> Thanks Charles Steltzlen to start the refactoring
>
[...]

> Modified: ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilFormatOut.java
> URL: http://svn.apache.org/viewvc/ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilFormatOut.java?rev=1864832&r1=1864831&r2=1864832&view=diff
> ==============================================================================
> --- ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilFormatOut.java (original)
> +++ ofbiz/ofbiz-framework/trunk/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilFormatOut.java Fri Aug  9 20:37:11 2019

[...]

> @@ -34,16 +34,11 @@ import java.util.TimeZone;
>  public final class UtilFormatOut {
>  
>      public static final String module = UtilFormatOut.class.getName();
> -
> -    // ------------------- price format handlers -------------------
> -    // FIXME: This is not thread-safe! DecimalFormat is not synchronized.
> -    private static final DecimalFormat priceDecimalFormat = new DecimalFormat(UtilProperties.getPropertyValue("general", "currency.decimal.format", "#,##0.00"));
> -
> -    // ------------------- quantity format handlers -------------------
> -    private static final DecimalFormat quantityDecimalFormat = new DecimalFormat("#,##0.###");
> -
> -    // ------------------- percentage format handlers -------------------
> -    private static final DecimalFormat percentageDecimalFormat = new DecimalFormat("##0.##%");
> +    public static final String DEFAULT_FORMAT = "default";
> +    public static final String AMOUNT_FORMAT = "amount";
> +    public static final String QUANTITY_FORMAT = "quantity";
> +    public static final String PERCENTAGE_FORMAT = "percentage";
> +    public static final String SPELLED_OUT_FORMAT = "spelled-out";

Intuitively an ‘enum’ would seem more appropriate to make the relation
between those constants clearer.

--8<---------------cut here---------------start------------->8---
enum Format {
   DEFAULT, AMOUNT, QUANTITY, PERCENTAGE, SPELLED_OUT;

   // Converts a string to a typed value.
   static Format valueOf(String name) {
      ...   
   }
}
--8<---------------cut here---------------end--------------->8---

Additionally I would be nice if the meaning of those “symbols” could be
documented inside the code either in the XSD with a reference in the
Java code, or in both in XSD and Java.

>  
>      private UtilFormatOut() {}
>  
> @@ -54,43 +49,70 @@ public final class UtilFormatOut {
>          return "";
>      }
>  
> -    /** Formats a Double representing a price into a string
> -     * @param price The price Double to be formatted
> -     * @return A String with the formatted price
> +    /** Format a number with format type define by properties
> +     *

Please expand the javadoc instead of removing it.

>       */
> -    public static String formatPrice(Double price) {
> -        if (price == null) {
> +    public static String formatNumber(Double number, String formatType, Delegator delegator, Locale locale) {

The dependency on the Delegator should be avoided if possible because it
makes writing unit complex tests. By the way where are those tests ? :-)

To remove a dependency on a class which is hard to instantiate like
‘Delegator’, the common solution is to replace it with an interface
(ideally a functional interface to let the caller pass a lambda).

In this particular case after a quick look, the ‘delegator’ argument
could potentially be replaced by a ‘templateProvider’ argument of type
‘Function<String, String>’ where the input is a format type and the
output is a template.

When things are complicated to decouple, an alternative is write an
overload specifically for the test and use it inside the coupled one.

> +        if (number == null) {
>              return "";
>          }
> -        return formatPrice(price.doubleValue());
> +        if (formatType == null) {
> +            formatType = DEFAULT_FORMAT;
> +        }
> +        if (locale == null) {
> +            locale = Locale.getDefault();
> +        }

Does silencing ‘null’ values really make sense here? if ‘null’ values
doesn't have sound meaning, I would recommend bailing out early instead
of silencing a possible programming mistake or worse let another method
downstream throw a cryptic exception later.  To bail out early you can
use things like ‘Objects.requireNonNull(myArg)’.

Have a nice weekend!

-- 
Mathieu Lirzin
GPG: F2A3 8D7E EB2B 6640 5761  070D 0ADE E100 9460 4D37