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-plugins.git


The following commit(s) were added to refs/heads/trunk by this push:
     new f2cb89730 Improved: Add a multi parameter dependent drop-down example 
(OFBIZ-11724) (#353)
f2cb89730 is described below

commit f2cb897306cc648be583af0f686df175a958fa62
Author: toaditi <[email protected]>
AuthorDate: Wed Aug 5 13:50:00 2026 +0530

    Improved: Add a multi parameter dependent drop-down example (OFBIZ-11724) 
(#353)
    
    Thanks to Pierre Smits for reporting OFBIZ-11724 and for identifying
    that only the first parameter of the list was being used.
    
    ### ⚠️ Please merge apache/ofbiz-framework#1547 first
    
    This is not merely inert without the framework fix — it is actively
    misleading. Against unfixed framework code the screen still renders and
    still looks like it works, but the generated selector is:
    
    ```
    #DependentDropDownMultiParamExampleForm_exampleId,asOfDate
    ```
    
    `asOfDate` there parses as a CSS *type* selector, matches nothing, and
    the date is silently never sent, so the service quietly falls back to
    "now". Merged on its own, this would ship a demo screen that documents
    the bug rather than the fix.
    
    ### Why an example was needed
    
    The reason OFBIZ-11724 sat open since 2020 is that nothing in OFBiz
    passes two parameters to a dependent drop-down, so there was nothing to
    click that failed. All 14 existing callers pass a single
    `countryGeoId`-style value.
    
    The `example` component even told readers, in
    `FormWidgetExampleScreens.xml`, to look at `EditProductPriceRules` if
    they needed more than one drop-down — and that screen sidesteps the
    template entirely, hand-writing the `getDependentDropdownValues` calls
    with fully-qualified ids. The workaround had quietly become the
    documented path. This PR updates that comment to point at the new
    example instead.
    
    ### What it does
    
    Pick an Example and an "as of" date; the feature drop-down lists only
    the features applied to that Example on that date. `ExampleFeatureAppl`
    is date-ranged, so this exercises `filterByDate` for real rather than
    just passing two arbitrary values — which is the case the issue is
    actually named after.
    
    - `getAssociatedExampleFeatures` service (`exampleId` + `asOfDate` →
    `exampleFeatureList`), with a JSON request-map
    - `DependentDropDownMultiParamExampleForm` and its screen section, using
    `paramKey` and `mainId` of `exampleId,asOfDate`
    - `ExampleFeature` / `ExampleFeatureAppl` demo rows with deliberately
    non-overlapping date ranges, so changing the date visibly changes the
    result: EX01 shows "Colour: red" in 2011, "Colour: blue" in 2013, and
    "Size: large" today
    - `testGetAssociatedExampleFeaturesFiltersByDate`, which asserts that
    two different dates return two different non-empty lists, plus an empty
    one outside both ranges — so a service that ignored the date would fail
    it
    
    The screen comment also spells out something the existing example does
    not: `paramKey` and `mainId` are paired **by position**, so the two
    lists must be given in the same order. Getting that wrong swaps values
    silently with no error.
    
    ### Testing
    
    `gradlew "ofbiz --test component=example --test
    suitename=example-tests"` — 27 tests, 0 failures, 0 errors.
    
    Verified on a local instance that the generated script now prefixes both
    field names, that the service returns different features for 2011, 2013
    and today over HTTP, and that the pre-existing single-parameter example
    on the same page is byte-identical to before the framework change.
    
    ### Known limitation
    
    Picking a date from the **calendar** refreshes the drop-down;
    **hand-typing** a date does not. This is pre-existing, framework-wide
    `date-time` behaviour rather than anything this change introduces: the
    datepicker is bound to the hidden canonical input and fires `change` on
    it, but the typed-input path in `OfbizUtil.js` has no
    `.trigger('change')`. Fixing it would change behaviour for every date
    field in OFBiz, so it does not belong in this issue — I will raise it
    separately and link it.
    
    To be precise about the evidence: I established the calendar path by
    reading the vendored `jquery-ui.js` `_selectDate` and the rendered
    markup, not by observing it in a browser.
    
    Happy to adjust any of this if a different approach is preferred.
---
 example/config/ExampleUiLabels.xml                 |  9 ++++++
 example/data/ExampleDemoData.xml                   | 13 ++++++++
 example/servicedef/services.xml                    |  8 +++++
 .../ofbiz/example/ExampleServicesScript.groovy     | 26 +++++++++++++++
 .../apache/ofbiz/example/test/ExampleTests.groovy  | 27 ++++++++++++++++
 example/testdef/data/ExampleTestData.xml           |  7 ++++
 example/webapp/example/WEB-INF/controller.xml      |  9 ++++++
 example/widget/example/FormWidgetExampleForms.xml  | 24 +++++++++++++-
 .../widget/example/FormWidgetExampleScreens.xml    | 37 ++++++++++++++++++++--
 9 files changed, 156 insertions(+), 4 deletions(-)

diff --git a/example/config/ExampleUiLabels.xml 
b/example/config/ExampleUiLabels.xml
index 9c8b55474..60aa9130c 100644
--- a/example/config/ExampleUiLabels.xml
+++ b/example/config/ExampleUiLabels.xml
@@ -55,6 +55,9 @@
         <value xml:lang="zh">样例应用</value>
         <value xml:lang="zh-TW">範例應用</value>
     </property>
+    <property key="ExampleAsOfDate">
+        <value xml:lang="en">As of date</value>
+    </property>
     <property key="ExampleAutocompleteDropdown">
         <value xml:lang="en">Autocomplete Dropdown</value>
         <value xml:lang="fr">Liste déroulante à auto complétion</value>
@@ -332,6 +335,12 @@
         <value xml:lang="zh">相互依赖的下拉框</value>
         <value xml:lang="zh-TW">相互依賴的下拉清單</value>
     </property>
+    <property key="ExampleMultiParamDependentDropDowns">
+        <value xml:lang="en">Dependent drop-down driven by two parent 
fields</value>
+    </property>
+    <property key="ExampleMultiParamDependentDropDownTooltip">
+        <value xml:lang="en">Pick an Example and a date. The feature drop-down 
lists only the features applied to that Example on that date, so both parent 
fields are sent to the service.</value>
+    </property>
     <property key="ExampleDisabledFields">
         <value xml:lang="en">Disabled Fields</value>
     </property>
diff --git a/example/data/ExampleDemoData.xml b/example/data/ExampleDemoData.xml
index 65de8a9d0..f744b9f4a 100644
--- a/example/data/ExampleDemoData.xml
+++ b/example/data/ExampleDemoData.xml
@@ -90,4 +90,17 @@ under the License.
     <ExampleStatus exampleId="EX02" statusDate="2010-01-02 00:00:00" 
statusEndDate="2011-01-02 00:00:00" statusId="EXST_IN_DESIGN"/>
     <ExampleStatus exampleId="EX02" statusDate="2011-01-02 00:00:01" 
statusEndDate="2012-01-02 00:00:00" statusId="EXST_DEFINED"/>
     <ExampleStatus exampleId="EX02" statusDate="2012-01-02 00:00:00" 
statusEndDate="2013-01-02 00:00:00" statusId="EXST_APPROVED"/>
+
+    <!-- Features for the multi parameter dependent drop-down example 
(OFBIZ-11724).
+         The date ranges deliberately do not overlap, so changing the date on 
the
+         Form Widget Examples screen visibly changes the drop-down contents. 
-->
+    <ExampleFeature exampleFeatureId="EXFT01" 
featureSourceEnumId="EXFTSRC_CUSTOMER" description="Colour: red"/>
+    <ExampleFeature exampleFeatureId="EXFT02" 
featureSourceEnumId="EXFTSRC_CUSTOMER" description="Colour: blue"/>
+    <ExampleFeature exampleFeatureId="EXFT03" 
featureSourceEnumId="EXFTSRC_PARTNER" description="Size: large"/>
+    <ExampleFeature exampleFeatureId="EXFT04" 
featureSourceEnumId="EXFTSRC_EMPLOYEE" description="Finish: matte"/>
+
+    <ExampleFeatureAppl exampleId="EX01" exampleFeatureId="EXFT01" 
fromDate="2010-01-01 00:00:00" thruDate="2012-01-01 00:00:00" 
exampleFeatureApplTypeId="REQUIRED" sequenceNum="1"/>
+    <ExampleFeatureAppl exampleId="EX01" exampleFeatureId="EXFT02" 
fromDate="2012-01-01 00:00:00" thruDate="2014-01-01 00:00:00" 
exampleFeatureApplTypeId="DESIRED" sequenceNum="2"/>
+    <ExampleFeatureAppl exampleId="EX01" exampleFeatureId="EXFT03" 
fromDate="2014-01-01 00:00:00" exampleFeatureApplTypeId="REQUIRED" 
sequenceNum="3"/>
+    <ExampleFeatureAppl exampleId="EX02" exampleFeatureId="EXFT04" 
fromDate="2010-01-01 00:00:00" exampleFeatureApplTypeId="DESIRED" 
sequenceNum="1"/>
 </entity-engine-xml>
diff --git a/example/servicedef/services.xml b/example/servicedef/services.xml
index 66227d0c8..bd0d25c77 100644
--- a/example/servicedef/services.xml
+++ b/example/servicedef/services.xml
@@ -53,6 +53,14 @@ under the License.
         <auto-attributes include="pk" mode="IN" optional="true"/>
         <attribute name="exampleList" mode="OUT" optional="true" type="List"/>
     </service>
+    <service name="getAssociatedExampleFeatures" engine="groovy" auth="true"
+            
location="component://example/src/main/groovy/org/apache/ofbiz/example/ExampleServicesScript.groovy"
 invoke="getAssociatedExampleFeatures">
+        <description>List the ExampleFeatures applied to an Example and active 
on a given date.
+            Backs the multi parameter dependent drop-down example 
(OFBIZ-11724).</description>
+        <attribute name="exampleId" mode="IN" type="String" optional="false"/>
+        <attribute name="asOfDate" mode="IN" type="Timestamp" optional="true"/>
+        <attribute name="exampleFeatureList" mode="OUT" type="List"/>
+    </service>
     <service name="createExampleStatus" default-entity-name="ExampleStatus" 
engine="groovy"
             
location="component://example/src/main/groovy/org/apache/ofbiz/example/ExampleServicesScript.groovy"
 invoke="createExampleStatus" auth="true">
         <description>Create a ExampleStatus</description>
diff --git 
a/example/src/main/groovy/org/apache/ofbiz/example/ExampleServicesScript.groovy 
b/example/src/main/groovy/org/apache/ofbiz/example/ExampleServicesScript.groovy
index a34204e80..555fd346d 100644
--- 
a/example/src/main/groovy/org/apache/ofbiz/example/ExampleServicesScript.groovy
+++ 
b/example/src/main/groovy/org/apache/ofbiz/example/ExampleServicesScript.groovy
@@ -18,6 +18,8 @@
  */
 package org.apache.ofbiz.example
 
+import java.sql.Timestamp
+
 import org.apache.ofbiz.base.util.UtilDateTime
 import org.apache.ofbiz.entity.GenericValue
 
@@ -88,3 +90,27 @@ Map createExampleStatus() {
 
     return result
 }
+
+Map getAssociatedExampleFeatures() {
+    Map result = success()
+    List exampleFeatureList = []
+
+    Timestamp asOfDate = parameters.asOfDate ?: UtilDateTime.nowTimestamp()
+
+    List<GenericValue> featureAppls = from('ExampleFeatureAppl')
+            .where('exampleId', parameters.exampleId)
+            .filterByDate(asOfDate)
+            .orderBy('sequenceNum')
+            .queryList()
+
+    for (GenericValue featureAppl : featureAppls) {
+        GenericValue exampleFeature = 
featureAppl.getRelatedOne('ExampleFeature', true)
+        if (exampleFeature) {
+            exampleFeatureList << [exampleFeatureId: 
exampleFeature.exampleFeatureId,
+                                   description: exampleFeature.description]
+        }
+    }
+
+    result.exampleFeatureList = exampleFeatureList
+    return result
+}
diff --git 
a/example/src/test/groovy/org/apache/ofbiz/example/test/ExampleTests.groovy 
b/example/src/test/groovy/org/apache/ofbiz/example/test/ExampleTests.groovy
index 24d42ea59..eb71ad660 100644
--- a/example/src/test/groovy/org/apache/ofbiz/example/test/ExampleTests.groovy
+++ b/example/src/test/groovy/org/apache/ofbiz/example/test/ExampleTests.groovy
@@ -18,6 +18,8 @@
  */
 package org.apache.ofbiz.example.test
 
+import java.sql.Timestamp
+
 import org.apache.ofbiz.entity.GenericValue
 import org.apache.ofbiz.service.ServiceUtil
 import org.apache.ofbiz.service.testtools.OFBizTestCase
@@ -75,4 +77,29 @@ class ExampleTests extends OFBizTestCase {
         assert example == null
     }
 
+    void testGetAssociatedExampleFeaturesFiltersByDate() {
+        GenericValue userLogin = from('UserLogin').where('userLoginId', 
'system').queryOne()
+        String serviceName = 'getAssociatedExampleFeatures'
+        String exampleId = 'EXTEST01'
+
+        // 2011 falls inside EXFTTEST01's range only
+        Map<String, Object> serviceResult = dispatcher.runSync(serviceName,
+                [exampleId: exampleId, asOfDate: Timestamp.valueOf('2011-06-01 
00:00:00'), userLogin: userLogin])
+        assert ServiceUtil.isSuccess(serviceResult)
+        assert serviceResult.exampleFeatureList*.exampleFeatureId == 
['EXFTTEST01']
+        assert serviceResult.exampleFeatureList[0].description == 'Test 
feature one'
+
+        // 2013 falls inside EXFTTEST02's range only, proving asOfDate is 
honoured
+        serviceResult = dispatcher.runSync(serviceName,
+                [exampleId: exampleId, asOfDate: Timestamp.valueOf('2013-06-01 
00:00:00'), userLogin: userLogin])
+        assert ServiceUtil.isSuccess(serviceResult)
+        assert serviceResult.exampleFeatureList*.exampleFeatureId == 
['EXFTTEST02']
+
+        // 2020 falls outside both ranges
+        serviceResult = dispatcher.runSync(serviceName,
+                [exampleId: exampleId, asOfDate: Timestamp.valueOf('2020-06-01 
00:00:00'), userLogin: userLogin])
+        assert ServiceUtil.isSuccess(serviceResult)
+        assert serviceResult.exampleFeatureList.isEmpty()
+    }
+
 }
diff --git a/example/testdef/data/ExampleTestData.xml 
b/example/testdef/data/ExampleTestData.xml
index 25bcefc29..1d10b0c70 100644
--- a/example/testdef/data/ExampleTestData.xml
+++ b/example/testdef/data/ExampleTestData.xml
@@ -30,4 +30,11 @@ under the License.
     <ExampleType exampleTypeId="CONTRIVED" description="Contrived Example 
Type"/>
     <Example exampleId="TestExampleUpdate" exampleTypeId="CONTRIVED" 
statusId="EXST_IN_DESIGN" exampleName="Test Example for Update"/>
     <Example exampleId="TestExampleDelete" exampleTypeId="CONTRIVED" 
statusId="EXST_IN_DESIGN" exampleName="Test Example for Delete"/>
+
+    <!-- Fixtures for testGetAssociatedExampleFeaturesFiltersByDate 
(OFBIZ-11724) -->
+    <Example exampleId="EXTEST01" exampleName="Multi param dependent drop-down 
test" exampleTypeId="CONTRIVED" statusId="EXST_IN_DESIGN"/>
+    <ExampleFeature exampleFeatureId="EXFTTEST01" 
featureSourceEnumId="EXFTSRC_CUSTOMER" description="Test feature one"/>
+    <ExampleFeature exampleFeatureId="EXFTTEST02" 
featureSourceEnumId="EXFTSRC_CUSTOMER" description="Test feature two"/>
+    <ExampleFeatureAppl exampleId="EXTEST01" exampleFeatureId="EXFTTEST01" 
fromDate="2010-01-01 00:00:00" thruDate="2012-01-01 00:00:00" 
exampleFeatureApplTypeId="REQUIRED" sequenceNum="1"/>
+    <ExampleFeatureAppl exampleId="EXTEST01" exampleFeatureId="EXFTTEST02" 
fromDate="2012-01-01 00:00:00" thruDate="2014-01-01 00:00:00" 
exampleFeatureApplTypeId="DESIRED" sequenceNum="2"/>
 </entity-engine-xml>
diff --git a/example/webapp/example/WEB-INF/controller.xml 
b/example/webapp/example/WEB-INF/controller.xml
index 464f30a7b..219128852 100644
--- a/example/webapp/example/WEB-INF/controller.xml
+++ b/example/webapp/example/WEB-INF/controller.xml
@@ -248,6 +248,15 @@ under the License.
 
     <!-- end of request mappings -->
 
+    <!--========================== AJAX events =====================-->
+    <!-- Get ExampleFeatures applied to an Example, active on a given date -->
+    <request-map uri="getAssociatedExampleFeatures">
+        <security https="true" auth="true"/>
+        <event type="service" invoke="getAssociatedExampleFeatures"/>
+        <response name="success" type="request" value="json"/>
+        <response name="error" type="request" value="json"/>
+    </request-map>
+
     <!-- View Mappings -->
     <view-map name="main" type="screen" 
page="component://example/widget/example/CommonScreens.xml#main"/>
 
diff --git a/example/widget/example/FormWidgetExampleForms.xml 
b/example/widget/example/FormWidgetExampleForms.xml
index 8d4c60c58..cb5d58f3d 100644
--- a/example/widget/example/FormWidgetExampleForms.xml
+++ b/example/widget/example/FormWidgetExampleForms.xml
@@ -325,7 +325,29 @@ under the License.
                     <entity-order-by field-name="geoName"/>
                 </entity-options>
             </drop-down>
-        </field>        
+        </field>
+    </form>
+
+    <!-- ***************************************************** -->
+    <!-- ***   Dependent dropdown with several parameters  *** -->
+    <!-- ***************************************************** -->
+    <form name="DependentDropDownMultiParamExampleForm" type="single" title="">
+        <field name="multiParamDependentDropDownFields" 
title="${uiLabelMap.ExampleMultiParamDependentDropDowns}">
+            <display 
description="${uiLabelMap.ExampleMultiParamDependentDropDownTooltip}"/>
+        </field>
+        <field name="exampleId" title="${uiLabelMap.ExampleExampleId}">
+            <drop-down>
+                <entity-options entity-name="Example" 
key-field-name="exampleId" description="${exampleName}">
+                    <entity-order-by field-name="exampleId"/>
+                </entity-options>
+            </drop-down>
+        </field>
+        <field name="asOfDate" title="${uiLabelMap.ExampleAsOfDate}">
+            <date-time type="date" 
default-value="${groovy:org.apache.ofbiz.base.util.UtilDateTime.nowTimestamp()}"/>
+        </field>
+        <field name="exampleFeatureId" title="${uiLabelMap.ExampleFeature}">
+            <drop-down allow-empty="false"/>
+        </field>
     </form>
 
     <!-- Simple form to print a FOP PDF file with the  fonts available in 
OFBiz Project -->
diff --git a/example/widget/example/FormWidgetExampleScreens.xml 
b/example/widget/example/FormWidgetExampleScreens.xml
index 909b7546a..90c6a63a2 100644
--- a/example/widget/example/FormWidgetExampleScreens.xml
+++ b/example/widget/example/FormWidgetExampleScreens.xml
@@ -55,9 +55,9 @@ under the License.
                                     </container>
                                     <section>
                                         <actions>
-                                            <!-- fields for 
SetDependentDropdownValuesJs.ftl, it's a try on generalization,
-                                                if you need an example with 
more than one dropdown in a form have a look at 
-                                                EditProductPriceRules screen 
in PriceScreens.xml-->
+                                            <!-- fields for 
SetDependentDropdownValuesJs.ftl.
+                                                For a dependent drop-down 
driven by more than one parent field, see the
+                                                
DependentDropDownMultiParamExampleForm section further down this screen. -->
                                             <set field="dependentForm" 
value="DropDownFieldsExampleForm"/>
                                             <set field="paramKey" 
value="countryGeoId"/>
                                             <set field="mainId" 
value="countryGeoId"/>
@@ -84,6 +84,37 @@ under the License.
                                         </widgets>
                                     </section>
                                 </container>
+                                <container style="screenlet-body">
+                                    <container style="button-bar"><label 
style="h2">${uiLabelMap.ExampleMultiParamDependentDropDowns}</label></container>
+                                    <container style="screenlet-body">
+                                        <label 
style="h3">${uiLabelMap.ExampleSourceCode}</label>
+                                        
<label>${uiLabelMap.ExampleDefinitionName} 
&quot;DependentDropDownMultiParamExampleForm&quot; 
${uiLabelMap.ExampleDefinitionFile}</label>
+                                        <link 
target="${formWidgetExampleFormsUrl}" url-mode="plain" target-window="svnPage" 
text="${uiLabelMap.ExampleDefinitionFileTooltip}" style="buttontext"/>
+                                    </container>
+                                    <section>
+                                        <actions>
+                                            <!-- A dependent drop-down driven 
by TWO parent fields (OFBIZ-11724).
+                                                 paramKey and mainId are comma 
separated lists, and the two lists are
+                                                 paired up by position: the 
Nth paramKey goes with the Nth mainId, so
+                                                 they must be listed in the 
same order. SetDependentDropdownValuesJs.ftl
+                                                 prefixes each field with the 
form name, so the service receives both
+                                                 exampleId and asOfDate and 
can filter ExampleFeatureAppl by date. -->
+                                            <set field="dependentForm" 
value="DependentDropDownMultiParamExampleForm"/>
+                                            <set field="paramKey" 
value="exampleId,asOfDate"/>
+                                            <set field="mainId" 
value="exampleId,asOfDate"/>
+                                            <set field="dependentId" 
value="exampleFeatureId"/>
+                                            <set field="requestName" 
value="getAssociatedExampleFeatures"/>
+                                            <set field="responseName" 
value="exampleFeatureList"/>
+                                            <set field="dependentKeyName" 
value="exampleFeatureId"/>
+                                            <set field="descName" 
value="description"/>
+                                            <set 
field="selectedDependentOption" value="_none_"/>
+                                        </actions>
+                                        <widgets>
+                                            
<platform-specific><html><html-template multi-block="true" 
location="component://common-theme/template/includes/SetDependentDropdownValuesJs.ftl"/></html></platform-specific>
+                                            <include-form 
name="DependentDropDownMultiParamExampleForm" 
location="component://example/widget/example/FormWidgetExampleForms.xml"/>
+                                        </widgets>
+                                    </section>
+                                </container>
                                 <container style="screenlet-body">
                                     <container style="button-bar"><label 
style="h2">${uiLabelMap.ExampleLookupFieldsTitle}</label></container>
                                         <container style="screenlet-body">

Reply via email to