Skip to Main Content

E-Business Suite

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

Interested in getting your voice heard by members of the Developer Marketing team at Oracle? Check out this post for AppDev or this post for AI focus group information.

ODEE BatchingSelectRule - Undocumented Behavior + Demo Java Unit Tester

Kevin MurphyMay 22 2025 — edited May 22 2025

I've recently done some extensive testing on my team's hundreds of bchingselelectrule FTL statements.

To mimic the behavior of Documaker in a java unit test requires some knowledge that isn't documented.

The behavior is sensible but completely undocumented.

Basic Info :

12.7.2 uses Freemarker 2.3.16.

The entire set of bching selectrule where active =1 is treated as a single freemarker template.

The freemarker template resolution is a three-pass template resolution process.

  1. The base freemarker statement.

    1. '${TRNS.KEY2!}'=='CPD'
      
  2. In the first pass it generates new Freemarker templates based off the bchingselectrules and the input data.

    1. 'RES'=='CPD'
      
  3. In the second pass, it wraps the Freemarker templates into boolean statements.

    1. <#if ('RES'=='CPD')>'TRUE'<#/if><#/if>
      
      1. This is probably not exactly what happens under the hood, but it is extremely close.
      2. I have no idea why freemarker requires two closing ifs in this statement, but it does.
  4. In the third pass, docfactory evaluates the freemarker expression from Step 3a.

    1. If more than 1 bchingselelectrule returns TRUE for a RCPS entry, throw an error.
    2. If less than 1 bchingselelectrule returns TRUE for a RCPS entry, place the recipient in the Default batch.

Some demo code to unit test from an exported bchingselectrule column from a BCHINGS table using FTL 2.3.33 jar from maven.

To setup, export only the BCHINGSELECTRULE column where Active=1 to “bchingselect.ftl” as a UTF-8 without BOM.

private static void processFTLTemplate(Configuration cfg, Map<String, Object> input){
StringWriter sw = new StringWriter(); 
BufferedWriter buff = new BufferedWriter(sw); 
cfg.setTemplateLoader(new FileTemplateLoader()); 
Template ftlTemp = cfg.getTemplate("bchingselect.ftl"); 
ftlTemp.process(input, buff); 
StringTemplateLoader stl=intermediatePassStringBuff(sw.toString)
int trueCount=secondPass(input, cfg,stl); 
	if (trueCount!=1){
	throw new RuntimeException("Failed on this!"); 
	} 
}
private static int secondPass(Map<String, Object> input, Configuration cfg, StringTemplateLoader stl) throws TemplateException, IOException { 
StringWriter sw = new StringWriter(); 
cfg.setTemplateLoader(stl); 
Template temp = cfg.getTemplate("test"); 
temp.process(input, sw); 
String s = sw.toString(); 
int trueCount=0; 
trueCount=StringUtils.countMatches(s,"TRUE"); 
if (trueCount!=1) { 
System.out.println("Found an extra entry"); 
Files.write(Paths.get("error.txt"), s.getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE, 
StandardOpenOption.TRUNCATE_EXISTING); 
} 
return trueCount; 
} 

private static StringTemplateLoader intermediatePassStringBuff(String string) throws IOException { 
String[] lines = string.split("\\R"); 
ArrayList<String> outLines = new ArrayList<>(); 
for (String s:lines){ 
StringBuilder sb=new StringBuilder(); 
sb.append("<#if (").append((s)); 
sb.append(")>'TRUE'<#/if></#if>"); 
outLines.add(sb.toString()); 
} 
String p = String.join("", outLines); 
StringTemplateLoader stl= new StringTemplateLoader(); 

stl.putTemplate("test",p); 
return stl; 
}

public static void main(String[] args) throws IOException { 
//Documaker 12_7_2 technically uses 2_3_16, but our only options are either 2_3_0 or 2_3_19. 
Configuration cfg = new Configuration(Configuration.VERSION_2_3_19); 

Map<String, Object> input = new HashMap<String, Object>(); 
//simple POJO with 1 attribute for demo purposes
RCPS rcps = new RCPS(); 
//simple POJO with 2 attributes for test purposes
TRNS trns = new TRNS(); 
trns.setKEY1("Test"); 
trns.setKEY2("CPD"); 
rcps.setRCPCUSINT001(0)
input.put("RCPS", rcps); 
input.put("TRNS", trns); 

processFTLTemplate(cfg,input)

}
Comments
Post Details
Added on May 22 2025
0 comments
83 views