This is an automated email from the ASF dual-hosted git repository.

arunpati pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 0a3c77ace1 Fixed: Call to non-existing method in production run PDF 
reports (OFBIZ-13504) (#1827)
0a3c77ace1 is described below

commit 0a3c77ace112bcc55dc0570a24af0dfb768be692
Author: toaditi <[email protected]>
AuthorDate: Wed Oct 7 18:32:14 2026 +0530

    Fixed: Call to non-existing method in production run PDF reports 
(OFBIZ-13504) (#1827)
    
    ## Credits
    
    Thanks to **Carsten Heinrigs** for reporting OFBIZ-13504 and for
    pinpointing both the
    template and the method involved, which made this quick to pick up.
    
    This follows up on the scope-reduction work in **OFBIZ-10478** by
    **Jacques Le Roux**
    and **Pradhan Yash Sharma**, reviewed by **Michael Brohl** -- a broad
    and valuable
    cleanup, in which this one method happened to have callers that static
    analysis
    could not see.
    
    ## The problem
    
    `UtilDateTime.toDateString(Date, String)` was narrowed from `public` to
    `private` in
    commit `abb4ead2f6` (OFBIZ-10478). FreeMarker resolves `Static["..."]`
    members
    reflectively and therefore only sees public methods, so the templates
    calling the
    two-argument overload can no longer resolve it:
    
    ```
    Java method "static 
org.apache.ofbiz.base.util.UtilDateTime.toDateString(Date)"
    takes 1 argument, but 2 was given.
    ```
    
    The render does not throw -- that message is written *into* the output
    stream, so in
    an `.fo.ftl` it lands inside the FO XML.
    
    ## Why restore the method rather than edit the templates
    
    The report originally suggested dropping the format string. That
    resolves to the
    single-argument overload, which is hardcoded to `MM/dd/yyyy`, so the
    reports would
    render without error but silently switch from day/month to month/day
    order.
    
    This was discussed on the Jira issue, and **Carsten Heinrigs, who
    reported it, agreed
    that restoring the method is the better route**, adding that he values
    being able to
    specify the date format string.
    
    That flexibility is really the crux of it: with the two-argument form
    private, a
    template that needs a particular format has no clean way to ask for one.
    Restoring it
    keeps the current output, fixes every call site at once, and leaves
    custom templates in
    existing deployments working, since they can call this documented helper
    too.
    
    ## Call sites fixed (7, across 3 components)
    
    - `applications/manufacturing/template/jobshopmgt/ProductionRun.fo.ftl`
    (4)
    -
    
`applications/manufacturing/template/reports/PRunsProductsAndOrder.fo.ftl:324`
    -
    `applications/manufacturing/template/reports/PRunsProductsStacks.fo.ftl:92`
    - `applications/content/template/lookup/ContentTreeLookupList.ftl:85`
    
    ## Verification
    
    Unit: new regression test in `FreeMarkerWorkerTests`, watched fail then
    pass. It uses
    9 March deliberately, so `dd/MM/yyyy` (09/03/2026) and `MM/dd/yyyy`
    (03/09/2026)
    differ -- the test therefore also fails if the format string is dropped.
    
    `./gradlew test checkstyleMain codenarcMain javadoc` -- BUILD
    SUCCESSFUL,
    854 tests, 0 failures, 0 errors, 0 skipped.
    
    Runtime: a production run was created through `createProductionRun` with
    a start
    date of 2026-03-09, then `PrintProductionRun` fetched over HTTP, with
    only the
    one-word change differing between the two runs.
    
    Worth noting that the broken case does not fail. Both runs return HTTP
    200 and a
    structurally valid one page `application/pdf`. The difference is what is
    printed
    in the date fields:
    
    | | before | after |
    | --- | --- | --- |
    | HTTP | 200 | 200 |
    | bytes | 6668 | 6276 |
    | Estimated Start Date | `Java method "static ...toDateString(Date)"
    takes 1 argument, but 2 was given` followed by an FTL stack trace |
    `09/03/2026` |
    
    So the report currently prints Java internals where the schedule dates
    belong,
    while the response itself still looks healthy.
    
    `release24.09` carries the same private modifier and the same call
    sites, so it
    likely needs the same change -- happy to raise a backport if that seems
    right.
---
 .../org/apache/ofbiz/base/util/UtilDateTime.java   | 15 ++++++++++++--
 .../base/util/template/FreeMarkerWorkerTests.java  | 23 ++++++++++++++++++++++
 2 files changed, 36 insertions(+), 2 deletions(-)

diff --git 
a/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilDateTime.java 
b/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilDateTime.java
index ded8bc626e..fcdc5bcbf1 100644
--- a/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilDateTime.java
+++ b/framework/base/src/main/java/org/apache/ofbiz/base/util/UtilDateTime.java
@@ -582,11 +582,22 @@ public final class UtilDateTime {
     }
 
     /**
-     * Makes a date String in the given from a Date
+     * Makes a date String in the given format from a Date.
+     *
+     * <p>This overload has no callers in Java code. It is reached from 
FreeMarker templates
+     * through {@code Static["org.apache.ofbiz.base.util.UtilDateTime"]}, 
which resolves members
+     * reflectively and therefore only sees public ones, so it must stay 
public. Narrowing it to
+     * private broke the production run reports (OFBIZ-13504); the failure is 
quiet, because
+     * FreeMarker writes the resolution error into the rendered output rather 
than throwing.
+     * Templates rely on passing an explicit pattern here: the single argument 
overload is fixed
+     * to {@code MM/dd/yyyy}, so dropping the pattern silently changes the 
rendered date order.</p>
+     *
      * @param date The Date
+     * @param format The date format pattern, as understood by {@link 
SimpleDateFormat}; the locale
+     *      default format is used when null
      * @return A date String in the given format
      */
-    private static String toDateString(java.util.Date date, String format) {
+    public static String toDateString(java.util.Date date, String format) {
         if (date == null) {
             return "";
         }
diff --git 
a/framework/base/src/test/java/org/apache/ofbiz/base/util/template/FreeMarkerWorkerTests.java
 
b/framework/base/src/test/java/org/apache/ofbiz/base/util/template/FreeMarkerWorkerTests.java
index 214c05b692..1b5ef0e72f 100644
--- 
a/framework/base/src/test/java/org/apache/ofbiz/base/util/template/FreeMarkerWorkerTests.java
+++ 
b/framework/base/src/test/java/org/apache/ofbiz/base/util/template/FreeMarkerWorkerTests.java
@@ -21,6 +21,7 @@ package org.apache.ofbiz.base.util.template;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 import java.io.StringWriter;
+import java.sql.Timestamp;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -41,4 +42,26 @@ public final class FreeMarkerWorkerTests {
         FreeMarkerWorker.renderTemplateFromString("template1", "Hello 
${name}", context, out, 0, false);
         assertEquals("Hello World!", out.toString());
     }
+
+    /**
+     * OFBIZ-13504: report templates format dates through
+     * {@code Static["...UtilDateTime"].toDateString(date, format)}. 
FreeMarker resolves
+     * {@code Static[...]} members reflectively against public methods only, 
so the two-argument
+     * overload must stay publicly visible for those templates to render.
+     *
+     * <p>The date below is deliberately the 9th of the 3rd month: {@code 
dd/MM/yyyy} yields
+     * 09/03/2026 while {@code MM/dd/yyyy} yields 03/09/2026. The assertion 
therefore also fails if
+     * the explicit format is dropped in favour of the single-argument 
overload, which silently
+     * switches the reports to US date order.</p>
+     */
+    @Test
+    public void renderTemplateCallingToDateStringWithExplicitFormat() throws 
Exception {
+        StringWriter out = new StringWriter();
+        Map<String, Object> context = new HashMap<>();
+        context.put("aDate", Timestamp.valueOf("2026-03-09 14:30:00"));
+        FreeMarkerWorker.renderTemplateFromString("templateToDateString",
+                
"${Static[\"org.apache.ofbiz.base.util.UtilDateTime\"].toDateString(aDate, 
\"dd/MM/yyyy\")}",
+                context, out, 0, false);
+        assertEquals("09/03/2026", out.toString());
+    }
 }

Reply via email to