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

meonkeys pushed a commit to branch asf-site
in repository https://gitbox.apache.org/repos/asf/fineract-site.git


The following commit(s) were added to refs/heads/asf-site by this push:
     new 61da315  update /docs/1.16.0-SNAPSHOT
61da315 is described below

commit 61da3153d8d32935628940a7b6da79e86fbd9069
Author: Adam Monsen <[email protected]>
AuthorDate: Mon Aug 31 09:47:58 2026 -0700

    update /docs/1.16.0-SNAPSHOT
    
    docs built with `./gradlew asciidoctor` from 
https://github.com/apache/fineract/commit/ef8ae79d4e39cca9a503a19e62b94efc6c7140e2
    
    change made with `fineract-site docs`
    
    Rebuilt so soon after 8120910dac35dead0b53b91ca9e0564fd2d5a550 because I 
noticed that build was missing a couple Vega-Lite diagrams... I built locally 
and ignored these messages:
    
        > Task :fineract-doc:asciidoctor
        Failed to generate image: Could not find the 'vg2svg' executable in 
PATH; add it to the PATH or specify its location using the 'vg2svg' document 
attribute :: ../../../build/generated/diagrams/main-sequence.vl.json :: 
/home/adamm/git/apache/fineract/fineract-doc/build/generated/diagrams//home/adamm/git/apache/fineract/fineract-doc/build/generated/diagrams/main-sequence.vl.json:0
 
(uri:classloader:/gems/asciidoctor-2.0.18/lib/asciidoctor/parser.rb:build_block)
        Failed to generate image: no implicit conversion of nil into String :: 
../../../build/generated/diagrams/distance-ranking.vl.json :: 
/home/adamm/git/apache/fineract/fineract-doc/build/generated/diagrams//home/adamm/git/apache/fineract/fineract-doc/build/generated/diagrams/distance-ranking.vl.json:0
 
(uri:classloader:/gems/asciidoctor-2.0.18/lib/asciidoctor/parser.rb:build_block)
        Failed to generate image: no implicit conversion of nil into String :: 
../../../build/generated/diagrams/cross-feature-matrix.vl.json :: 
/home/adamm/git/apache/fineract/fineract-doc/build/generated/diagrams//home/adamm/git/apache/fineract/fineract-doc/build/generated/diagrams/cross-feature-matrix.vl.json:0
 
(uri:classloader:/gems/asciidoctor-2.0.18/lib/asciidoctor/parser.rb:build_block)
    
    And under "Abstractness against instability, with the main sequence" I 
missed the raw JSON with "Failed to generate image: Could not find the 'vl2vg' 
executable in PATH; add it to the PATH or specify its location using the 
'vl2vg' document attribute".
    
    Note: Today I also wrote 
https://github.com/apache/fineract/pull/6315#issuecomment-5481448575 and filed 
https://issues.apache.org/jira/browse/FINERACT-2795 .
---
 docs/1.16.0-SNAPSHOT/index.html | 1799 ++++++++++++++++-----------------------
 1 file changed, 711 insertions(+), 1088 deletions(-)

diff --git a/docs/1.16.0-SNAPSHOT/index.html b/docs/1.16.0-SNAPSHOT/index.html
index f816785..4aa85cb 100644
--- a/docs/1.16.0-SNAPSHOT/index.html
+++ b/docs/1.16.0-SNAPSHOT/index.html
@@ -846,6 +846,7 @@ pre.rouge {
 <li><a href="#testing-cucumber">Cucumber E2E Tests</a></li>
 <li><a href="#_unit_testing">Unit Testing</a></li>
 <li><a href="#_integration_testing">Integration Testing</a></li>
+<li><a href="#_feign_integration_tests">Feign Integration Tests</a></li>
 </ul>
 </li>
 <li><a href="#_fineract_documentation_guide">Fineract Documentation Guide</a>
@@ -925,7 +926,7 @@ Email: <a 
href="mailto:[email protected]";>[email protected]</a></p>
 <p><strong>Version</strong>: 1.16.0-SNAPSHOT</p>
 </div>
 <div class="paragraph">
-<p><strong>Built on</strong>: Sat Aug 29 20:04:15 PDT 2026</p>
+<p><strong>Built on</strong>: Mon Aug 31 09:19:07 PDT 2026</p>
 </div>
 <div class="paragraph">
 <p>Note the version in the URL path:</p>
@@ -1207,7 +1208,7 @@ To overcome this problem, Fineract instances can be 
started in different instanc
 </div>
 <div class="imageblock">
 <div class="content">
-<img 
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOYAAAD8CAIAAADsRU8RAAAdR0lEQVR4Xu2deXRV1dmHLwEDREYTBqUC1iIWKFBqZagFKwtCyQqEoYgUNYupBQErIp8MMrlqkQY+pCAQJikYFFOLtAkQBMkAkSE2EaKEOSEDGZgSyEAS8/163nX3vewMHC73XPbO9z5/ZJ1hn3P3ft/n7L1P7smJrYJhtMImb2AYtWFlGc1wKPsDwygMK8toBivLaAYry2gGK8toBivLaAYry2gGK8toBivLaAYry2gGK8toBivLaAYry2gGK8toBivLaAYry2gGK8toBivLaAYry2gGK8toBivLaAYry2gGK8toBivLaAYry2gGK8toBivLaAYry2gGK8toBivLaAYry2gGK8toRq1S1tfX18aYA7GSw6cJtUpZZEK0gqkZxOr69esFBQWFhYUlJSVlZWVyNFXF
 [...]
+<img 
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOYAAAD8CAIAAADsRU8RAAAb6UlEQVR4Xu2de3gU1fmAlyCXXDFNCDwIiMWAAgXKk3Ix3ISHgpeICkKBolgQqoCCotZauVipPIp4oYhYAgqKFS+IAspFQYxco0VRASlIICYRJAmJJgQS83ufOd2z64TAkOyuc/L73j/ynJk5M3vO9717zpnNZOMpFwSj8Nh3CIK7EWUFw/Ap+5MguBhRVjAMUVYwDFFWMAxRVjAMUVYwDFFWMAxRVjAMUVYwDFFWMAxRVjAMUVYwDFFWMAxRVjAMUVYwDFFWMAxRVjAMUVYwDFFWMAxRVjAMUVYwDFFWMAxRVjAMUVYwDFFWMAxRVjAMUVYwDFFWMAxRVjAMUVYwDFFWMAxRVjCMGqVsXFycR3AGsbKHzxBqlLJkQvdCODvEKj8/v7CwsKioqKSkpLS01B5Nt+Lr
 [...]
 </div>
 </div>
 </div>
@@ -1219,7 +1220,7 @@ In this case, the write instance and the database will be 
freed from part of the
 </div>
 <div class="imageblock">
 <div class="content">
-<img 
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAuQAAAFCCAIAAAA2a19cAAA+5ElEQVR4Xu3deZhUxbk/8AZUFtk3cQMS4xJcIMarogZErmLgAREIwZ0LSsQAxiWoYGSJMaiIRlERwRURFRPlXkQ2o8gSVAQEVFCQQfZ9HZZB+X1//T5T51A909ZwuqZOdX8/f/Cc7q4+c7reb/V5u6eZThwiIiIiirGEfgURERFRnLBZISIiolgLmpUfiYiIiGKDzQoRERHFGpsVIiIiijU2K0RERBRrbFaIiIgo1tisEBERUayxWSEiIqJYY7NCREREscZmhYiIiGKNzQoRERHFGpsVIiIiijU2K0RERBRrbFaIiIgo1tisEBERUayxWSEiIqJYY7NCREREscZmhYiIiGKNzQoRERHFGpsVIiIiijU2K0RERBRrbFaIiIgo1tisEBERUayxWSEiIqJYY7NCRERE
 [...]
+<img 
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAuQAAAFCCAIAAAA2a19cAAA8lklEQVR4Xu3deZxUxbk+8AbFYV8EwRC2hE1HBOLlsogLyMegEtxAFIjbBcEFXFFcbhw0mhBBicGViCDiLlGTIAKuiCCKiqIiGpBRZBUYQBhGlvk9t+s3dY7VM2PB6ZqqOuf5/sGnprv6zOl6n+rz9ixMqpiIiIjIYSn1BiIiIiKXsFkhIiIipwXNyj4iIiIiZ7BZISIiIqexWSEiIiKnsVkhIiIip7FZISIiIqexWSEiIiKnsVkhIiIip7FZISIiIqexWSEiIiKnsVkhIiIip7FZISIiIqexWSEiIiKnsVkhIiIip7FZISIiIqexWSEiIiKnsVkhIiIip7FZISIiIqexWSEiIiKnsVkhIiIip7FZISIiIqexWSEiIiKnsVkhIiIip7FZISIiIqexWSEiIiKnsVkh
 [...]
 </div>
 </div>
 <div class="paragraph">
@@ -1230,7 +1231,7 @@ The best practice is to deploy 1 master batch instance 
and multiple worker batch
 </div>
 <div class="imageblock">
 <div class="content">
-<img 
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA4QAAAEYCAIAAACLF699AAA3lklEQVR4Xu3deZhU1Z3/8QsiAjabLBJNBHU0CTDAMEbBUYkyAgMDIhhExYQHhERFNG4/QUTQGUcd9GccVARRcQFDSOISQHCJNJsIKMgiIIusLfvS0gs06flOnafPLQ/dnUOdKqpO3ffrD55bVadud5/vpy+f3oNSAAAAIE0C8w4AAADgZKGMAgAAIG3CMvo3AAAA4KSgjAIAACBtKKMAAABIG8ooAAAA0oYyCgAAgLShjAIAACBtKKMAAABIG8ooAAAA0oYyCgAAgLShjAIAACBtKKMAAABIG8ooAAAA0oYyCgAAgLShjAIAACBtKKMAAABIG8ooAAAA0oYyCgAAgLShjAIAACBtKKMAAABIG8ooAAAA0oYyCgAAgLShjAIAACBtKKMAAABIG8ooAAAA0oYyCgAA
 [...]
+<img 
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA4QAAAEYCAIAAACLF699AAA1CUlEQVR4Xu3de3hU1b3/8QHlHm5NBA8C1cpFEIHDw+EiyEWOoli8oaJQLR5QqoJFpd5aUbS2PNZqrRcUBRUVDlZFVEDQKmBUULQoKqCiRC7hDoIQAsH8vr9ZJ2uPKwldzMpkZu39fv3Bs2Zmzc5kfz/Z+SQQEisGAAAA0iRm3gEAAABUFsooAAAA0iYooz8CAAAAlYIyCgAAgLShjAIAACBtKKMAAABIG8ooAAAA0oYyCgAAgLShjAIAACBtKKMAAABIG8ooAAAA0oYyCgAAgLShjAIAACBtKKMAAABIG8ooAAAA0oYyCgAAgLShjAIAACBtKKMAAABIG8ooAAAA0oYyCgAAgLShjAIAACBtKKMAAABIG8ooAAAA0oYyCgAAgLShjAIAACBtKKMAAABIG8ooAAAA
 [...]
 </div>
 </div>
 <div class="paragraph">
@@ -4192,951 +4193,23 @@ column B <strong>and</strong> row B / column A — is a 
<strong>cycle</strong>,
 the other. A column that is almost entirely filled marks a feature the whole 
platform<br>
 depends on, which needs stability rather than decoupling.</p>
 </div>
-<div class="listingblock">
-<div class="title">Abstractness against instability, with the main 
sequence</div>
+<div class="imageblock">
 <div class="content">
-<pre>Failed to generate image: Could not find the 'vl2vg' executable in PATH; 
add it to the PATH or specify its location using the 'vl2vg' document attribute
-{
-  "$schema": "https://vega.github.io/schema/vega-lite/v5.json";,
-  "title": "Abstractness vs Instability (distance from the main sequence)",
-  "width": 520,
-  "height": 420,
-  "data": {
-    "values": [
-      {
-        "component": "loan",
-        "A": 0.1181,
-        "I": 0.5077,
-        "D": 0.3742,
-        "classCount": 1507
-      },
-      {
-        "component": "account-transfers",
-        "A": 0.0742,
-        "I": 0.6916,
-        "D": 0.2342,
-        "classCount": 229
-      },
-      {
-        "component": "client",
-        "A": 0.1087,
-        "I": 0.4533,
-        "D": 0.438,
-        "classCount": 276
-      },
-      {
-        "component": "savings",
-        "A": 0.069,
-        "I": 0.5927,
-        "D": 0.3383,
-        "classCount": 783
-      },
-      {
-        "component": "accounting",
-        "A": 0.1101,
-        "I": 0.556,
-        "D": 0.334,
-        "classCount": 318
-      },
-      {
-        "component": "group",
-        "A": 0.0657,
-        "I": 0.6106,
-        "D": 0.3237,
-        "classCount": 274
-      },
-      {
-        "component": "organisation",
-        "A": 0.126,
-        "I": 0.109,
-        "D": 0.7651,
-        "classCount": 254
-      },
-      {
-        "component": "shareaccounts",
-        "A": 0.1308,
-        "I": 0.7829,
-        "D": 0.0862,
-        "classCount": 107
-      },
-      {
-        "component": "charge",
-        "A": 0.0533,
-        "I": 0.2821,
-        "D": 0.6646,
-        "classCount": 75
-      },
-      {
-        "component": "notifications",
-        "A": 0.1897,
-        "I": 0.9667,
-        "D": 0.1563,
-        "classCount": 58
-      },
-      {
-        "component": "calendar",
-        "A": 0.12,
-        "I": 0.3667,
-        "D": 0.5133,
-        "classCount": 50
-      },
-      {
-        "component": "batch",
-        "A": 0.0222,
-        "I": 0.7241,
-        "D": 0.2536,
-        "classCount": 90
-      },
-      {
-        "component": "teller",
-        "A": 0.0875,
-        "I": 0.9643,
-        "D": 0.0518,
-        "classCount": 80
-      },
-      {
-        "component": "rate",
-        "A": 0.125,
-        "I": 0.6341,
-        "D": 0.2409,
-        "classCount": 24
-      },
-      {
-        "component": "useradministration",
-        "A": 0.124,
-        "I": 0.1294,
-        "D": 0.7466,
-        "classCount": 121
-      },
-      {
-        "component": "report",
-        "A": 0.6667,
-        "I": 0.3333,
-        "D": 0.0,
-        "classCount": 6
-      },
-      {
-        "component": "template",
-        "A": 0.1154,
-        "I": 0.75,
-        "D": 0.1346,
-        "classCount": 52
-      }
-    ]
-  },
-  "layer": [
-    {
-      "data": {
-        "values": [
-          {
-            "I": 0,
-            "A": 1
-          },
-          {
-            "I": 1,
-            "A": 0
-          }
-        ]
-      },
-      "mark": {
-        "type": "line",
-        "strokeDash": [
-          6,
-          4
-        ],
-        "color": "#888"
-      },
-      "encoding": {
-        "x": {
-          "field": "I",
-          "type": "quantitative"
-        },
-        "y": {
-          "field": "A",
-          "type": "quantitative"
-        }
-      }
-    },
-    {
-      "mark": {
-        "type": "circle",
-        "opacity": 0.8
-      },
-      "encoding": {
-        "x": {
-          "field": "I",
-          "type": "quantitative",
-          "title": "Instability (I = Ce / (Ca + Ce))",
-          "scale": {
-            "domain": [
-              0,
-              1
-            ]
-          }
-        },
-        "y": {
-          "field": "A",
-          "type": "quantitative",
-          "title": "Abstractness (A)",
-          "scale": {
-            "domain": [
-              0,
-              1
-            ]
-          }
-        },
-        "size": {
-          "field": "classCount",
-          "type": "quantitative",
-          "title": "classes"
-        },
-        "color": {
-          "field": "D",
-          "type": "quantitative",
-          "title": "Distance",
-          "scale": {
-            "scheme": "orangered"
-          }
-        },
-        "tooltip": [
-          {
-            "field": "component",
-            "type": "nominal"
-          },
-          {
-            "field": "A",
-            "type": "quantitative"
-          },
-          {
-            "field": "I",
-            "type": "quantitative"
-          },
-          {
-            "field": "D",
-            "type": "quantitative"
-          },
-          {
-            "field": "classCount",
-            "type": "quantitative"
-          }
-        ]
-      }
-    },
-    {
-      "mark": {
-        "type": "text",
-        "dy": -10,
-        "fontSize": 9
-      },
-      "encoding": {
-        "x": {
-          "field": "I",
-          "type": "quantitative"
-        },
-        "y": {
-          "field": "A",
-          "type": "quantitative"
-        },
-        "text": {
-          "field": "component",
-          "type": "nominal"
-        }
-      }
-    }
-  ]
-}</pre>
+<img 
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj48c3ZnIGNsYXNzPSdtYXJrcycgaGVpZ2h0PSc0ODQnIHByZXNlcnZlQXNwZWN0UmF0aW89J3hNaWRZTWlkIG1lZXQnIHZlcnNpb249JzEuMScgdmlld0JveD0nMCAwIDYzNSA0ODQnIHdpZHRoPSc2MzUnIHhtbG5zOnhsaW5rPSdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPjxyZWN0IGZpbGw9
 [...]
 </div>
+<div class="title">Figure 4. Abstractness against instability, with the main 
sequence</div>
 </div>
-<div class="listingblock">
-<div class="title">Distance from the main sequence, by feature</div>
+<div class="imageblock">
 <div class="content">
-<pre>Failed to generate image:
-{
-  "$schema": "https://vega.github.io/schema/vega-lite/v5.json";,
-  "title": "Distance from the main sequence, by component",
-  "width": 520,
-  "height": {
-    "step": 14
-  },
-  "data": {
-    "values": [
-      {
-        "component": "loan",
-        "D": 0.3742,
-        "Ce": 461,
-        "Ca": 447
-      },
-      {
-        "component": "account-transfers",
-        "D": 0.2342,
-        "Ce": 148,
-        "Ca": 66
-      },
-      {
-        "component": "client",
-        "D": 0.438,
-        "Ce": 160,
-        "Ca": 193
-      },
-      {
-        "component": "savings",
-        "D": 0.3383,
-        "Ce": 227,
-        "Ca": 156
-      },
-      {
-        "component": "accounting",
-        "D": 0.334,
-        "Ce": 154,
-        "Ca": 123
-      },
-      {
-        "component": "group",
-        "D": 0.3237,
-        "Ce": 138,
-        "Ca": 88
-      },
-      {
-        "component": "organisation",
-        "D": 0.7651,
-        "Ce": 73,
-        "Ca": 597
-      },
-      {
-        "component": "shareaccounts",
-        "D": 0.0862,
-        "Ce": 101,
-        "Ca": 28
-      },
-      {
-        "component": "charge",
-        "D": 0.6646,
-        "Ce": 55,
-        "Ca": 140
-      },
-      {
-        "component": "notifications",
-        "D": 0.1563,
-        "Ce": 58,
-        "Ca": 2
-      },
-      {
-        "component": "calendar",
-        "D": 0.5133,
-        "Ce": 44,
-        "Ca": 76
-      },
-      {
-        "component": "batch",
-        "D": 0.2536,
-        "Ce": 42,
-        "Ca": 16
-      },
-      {
-        "component": "teller",
-        "D": 0.0518,
-        "Ce": 54,
-        "Ca": 2
-      },
-      {
-        "component": "rate",
-        "D": 0.2409,
-        "Ce": 26,
-        "Ca": 15
-      },
-      {
-        "component": "useradministration",
-        "D": 0.7466,
-        "Ce": 51,
-        "Ca": 343
-      },
-      {
-        "component": "report",
-        "D": 0.0,
-        "Ce": 4,
-        "Ca": 8
-      },
-      {
-        "component": "template",
-        "D": 0.1346,
-        "Ce": 12,
-        "Ca": 4
-      }
-    ]
-  },
-  "mark": "bar",
-  "encoding": {
-    "y": {
-      "field": "component",
-      "type": "nominal",
-      "sort": "-x",
-      "title": null
-    },
-    "x": {
-      "field": "D",
-      "type": "quantitative",
-      "title": "Distance (D = |A + I - 1|)"
-    },
-    "color": {
-      "field": "D",
-      "type": "quantitative",
-      "legend": null,
-      "scale": {
-        "scheme": "orangered"
-      }
-    },
-    "tooltip": [
-      {
-        "field": "component",
-        "type": "nominal"
-      },
-      {
-        "field": "D",
-        "type": "quantitative"
-      },
-      {
-        "field": "Ce",
-        "type": "quantitative"
-      },
-      {
-        "field": "Ca",
-        "type": "quantitative"
-      }
-    ]
-  }
-}</pre>
+<img 
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj48c3ZnIGNsYXNzPSdtYXJrcycgaGVpZ2h0PScyOTcnIHByZXNlcnZlQXNwZWN0UmF0aW89J3hNaWRZTWlkIG1lZXQnIHZlcnNpb249JzEuMScgdmlld0JveD0nMCAwIDYyOCAyOTcnIHdpZHRoPSc2MjgnIHhtbG5zOnhsaW5rPSdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPjxyZWN0IGZpbGw9
 [...]
 </div>
+<div class="title">Figure 5. Distance from the main sequence, by feature</div>
 </div>
-<div class="listingblock">
-<div class="title">Cross-feature dependencies — the row depends on the 
column</div>
+<div class="imageblock">
 <div class="content">
-<pre>Failed to generate image:
-{
-  "$schema": "https://vega.github.io/schema/vega-lite/v5.json";,
-  "title": "Cross-feature dependencies (row depends on column)",
-  "width": 520,
-  "height": 520,
-  "data": {
-    "values": [
-      {
-        "from": "loan",
-        "to": "account-transfers",
-        "v": 1
-      },
-      {
-        "from": "loan",
-        "to": "accounting",
-        "v": 1
-      },
-      {
-        "from": "loan",
-        "to": "calendar",
-        "v": 1
-      },
-      {
-        "from": "loan",
-        "to": "charge",
-        "v": 1
-      },
-      {
-        "from": "loan",
-        "to": "client",
-        "v": 1
-      },
-      {
-        "from": "loan",
-        "to": "group",
-        "v": 1
-      },
-      {
-        "from": "loan",
-        "to": "organisation",
-        "v": 1
-      },
-      {
-        "from": "loan",
-        "to": "rate",
-        "v": 1
-      },
-      {
-        "from": "loan",
-        "to": "savings",
-        "v": 1
-      },
-      {
-        "from": "loan",
-        "to": "teller",
-        "v": 1
-      },
-      {
-        "from": "loan",
-        "to": "useradministration",
-        "v": 1
-      },
-      {
-        "from": "account-transfers",
-        "to": "batch",
-        "v": 1
-      },
-      {
-        "from": "account-transfers",
-        "to": "calendar",
-        "v": 1
-      },
-      {
-        "from": "account-transfers",
-        "to": "client",
-        "v": 1
-      },
-      {
-        "from": "account-transfers",
-        "to": "group",
-        "v": 1
-      },
-      {
-        "from": "account-transfers",
-        "to": "loan",
-        "v": 1
-      },
-      {
-        "from": "account-transfers",
-        "to": "organisation",
-        "v": 1
-      },
-      {
-        "from": "account-transfers",
-        "to": "savings",
-        "v": 1
-      },
-      {
-        "from": "account-transfers",
-        "to": "shareaccounts",
-        "v": 1
-      },
-      {
-        "from": "account-transfers",
-        "to": "useradministration",
-        "v": 1
-      },
-      {
-        "from": "client",
-        "to": "account-transfers",
-        "v": 1
-      },
-      {
-        "from": "client",
-        "to": "accounting",
-        "v": 1
-      },
-      {
-        "from": "client",
-        "to": "charge",
-        "v": 1
-      },
-      {
-        "from": "client",
-        "to": "group",
-        "v": 1
-      },
-      {
-        "from": "client",
-        "to": "loan",
-        "v": 1
-      },
-      {
-        "from": "client",
-        "to": "organisation",
-        "v": 1
-      },
-      {
-        "from": "client",
-        "to": "savings",
-        "v": 1
-      },
-      {
-        "from": "client",
-        "to": "shareaccounts",
-        "v": 1
-      },
-      {
-        "from": "client",
-        "to": "useradministration",
-        "v": 1
-      },
-      {
-        "from": "savings",
-        "to": "account-transfers",
-        "v": 1
-      },
-      {
-        "from": "savings",
-        "to": "accounting",
-        "v": 1
-      },
-      {
-        "from": "savings",
-        "to": "calendar",
-        "v": 1
-      },
-      {
-        "from": "savings",
-        "to": "charge",
-        "v": 1
-      },
-      {
-        "from": "savings",
-        "to": "client",
-        "v": 1
-      },
-      {
-        "from": "savings",
-        "to": "group",
-        "v": 1
-      },
-      {
-        "from": "savings",
-        "to": "loan",
-        "v": 1
-      },
-      {
-        "from": "savings",
-        "to": "organisation",
-        "v": 1
-      },
-      {
-        "from": "savings",
-        "to": "useradministration",
-        "v": 1
-      },
-      {
-        "from": "accounting",
-        "to": "account-transfers",
-        "v": 1
-      },
-      {
-        "from": "accounting",
-        "to": "charge",
-        "v": 1
-      },
-      {
-        "from": "accounting",
-        "to": "client",
-        "v": 1
-      },
-      {
-        "from": "accounting",
-        "to": "loan",
-        "v": 1
-      },
-      {
-        "from": "accounting",
-        "to": "organisation",
-        "v": 1
-      },
-      {
-        "from": "accounting",
-        "to": "savings",
-        "v": 1
-      },
-      {
-        "from": "accounting",
-        "to": "shareaccounts",
-        "v": 1
-      },
-      {
-        "from": "accounting",
-        "to": "useradministration",
-        "v": 1
-      },
-      {
-        "from": "group",
-        "to": "account-transfers",
-        "v": 1
-      },
-      {
-        "from": "group",
-        "to": "calendar",
-        "v": 1
-      },
-      {
-        "from": "group",
-        "to": "client",
-        "v": 1
-      },
-      {
-        "from": "group",
-        "to": "loan",
-        "v": 1
-      },
-      {
-        "from": "group",
-        "to": "organisation",
-        "v": 1
-      },
-      {
-        "from": "group",
-        "to": "savings",
-        "v": 1
-      },
-      {
-        "from": "group",
-        "to": "useradministration",
-        "v": 1
-      },
-      {
-        "from": "organisation",
-        "to": "accounting",
-        "v": 1
-      },
-      {
-        "from": "organisation",
-        "to": "calendar",
-        "v": 1
-      },
-      {
-        "from": "organisation",
-        "to": "charge",
-        "v": 1
-      },
-      {
-        "from": "organisation",
-        "to": "client",
-        "v": 1
-      },
-      {
-        "from": "organisation",
-        "to": "loan",
-        "v": 1
-      },
-      {
-        "from": "organisation",
-        "to": "savings",
-        "v": 1
-      },
-      {
-        "from": "organisation",
-        "to": "useradministration",
-        "v": 1
-      },
-      {
-        "from": "shareaccounts",
-        "to": "account-transfers",
-        "v": 1
-      },
-      {
-        "from": "shareaccounts",
-        "to": "accounting",
-        "v": 1
-      },
-      {
-        "from": "shareaccounts",
-        "to": "charge",
-        "v": 1
-      },
-      {
-        "from": "shareaccounts",
-        "to": "client",
-        "v": 1
-      },
-      {
-        "from": "shareaccounts",
-        "to": "organisation",
-        "v": 1
-      },
-      {
-        "from": "shareaccounts",
-        "to": "savings",
-        "v": 1
-      },
-      {
-        "from": "shareaccounts",
-        "to": "useradministration",
-        "v": 1
-      },
-      {
-        "from": "charge",
-        "to": "accounting",
-        "v": 1
-      },
-      {
-        "from": "charge",
-        "to": "loan",
-        "v": 1
-      },
-      {
-        "from": "charge",
-        "to": "organisation",
-        "v": 1
-      },
-      {
-        "from": "charge",
-        "to": "savings",
-        "v": 1
-      },
-      {
-        "from": "charge",
-        "to": "shareaccounts",
-        "v": 1
-      },
-      {
-        "from": "charge",
-        "to": "useradministration",
-        "v": 1
-      },
-      {
-        "from": "notifications",
-        "to": "client",
-        "v": 1
-      },
-      {
-        "from": "notifications",
-        "to": "loan",
-        "v": 1
-      },
-      {
-        "from": "notifications",
-        "to": "organisation",
-        "v": 1
-      },
-      {
-        "from": "notifications",
-        "to": "savings",
-        "v": 1
-      },
-      {
-        "from": "notifications",
-        "to": "shareaccounts",
-        "v": 1
-      },
-      {
-        "from": "notifications",
-        "to": "useradministration",
-        "v": 1
-      },
-      {
-        "from": "calendar",
-        "to": "client",
-        "v": 1
-      },
-      {
-        "from": "calendar",
-        "to": "group",
-        "v": 1
-      },
-      {
-        "from": "calendar",
-        "to": "loan",
-        "v": 1
-      },
-      {
-        "from": "calendar",
-        "to": "organisation",
-        "v": 1
-      },
-      {
-        "from": "calendar",
-        "to": "useradministration",
-        "v": 1
-      },
-      {
-        "from": "batch",
-        "to": "client",
-        "v": 1
-      },
-      {
-        "from": "batch",
-        "to": "loan",
-        "v": 1
-      },
-      {
-        "from": "batch",
-        "to": "savings",
-        "v": 1
-      },
-      {
-        "from": "batch",
-        "to": "useradministration",
-        "v": 1
-      },
-      {
-        "from": "teller",
-        "to": "accounting",
-        "v": 1
-      },
-      {
-        "from": "teller",
-        "to": "client",
-        "v": 1
-      },
-      {
-        "from": "teller",
-        "to": "organisation",
-        "v": 1
-      },
-      {
-        "from": "teller",
-        "to": "useradministration",
-        "v": 1
-      },
-      {
-        "from": "rate",
-        "to": "charge",
-        "v": 1
-      },
-      {
-        "from": "rate",
-        "to": "loan",
-        "v": 1
-      },
-      {
-        "from": "rate",
-        "to": "useradministration",
-        "v": 1
-      },
-      {
-        "from": "useradministration",
-        "to": "client",
-        "v": 1
-      },
-      {
-        "from": "useradministration",
-        "to": "organisation",
-        "v": 1
-      }
-    ]
-  },
-  "mark": "rect",
-  "encoding": {
-    "y": {
-      "field": "from",
-      "type": "nominal",
-      "title": "depends on -&gt;"
-    },
-    "x": {
-      "field": "to",
-      "type": "nominal",
-      "title": null,
-      "axis": {
-        "labelAngle": -45,
-        "orient": "top"
-      }
-    },
-    "color": {
-      "value": "#d1495b"
-    },
-    "tooltip": [
-      {
-        "field": "from",
-        "type": "nominal"
-      },
-      {
-        "field": "to",
-        "type": "nominal"
-      }
-    ]
-  }
-}</pre>
+<img 
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnPz4KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj48c3ZnIGNsYXNzPSdtYXJrcycgaGVpZ2h0PSc2MjUnIHByZXNlcnZlQXNwZWN0UmF0aW89J3hNaWRZTWlkIG1lZXQnIHZlcnNpb249JzEuMScgdmlld0JveD0nMCAwIDY4NyA2MjUnIHdpZHRoPSc2ODcnIHhtbG5zOnhsaW5rPSdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnPjxyZWN0IGZpbGw9
 [...]
 </div>
+<div class="title">Figure 6. Cross-feature dependencies — the row depends on 
the column</div>
 </div>
 <div class="paragraph">
 <p>Three readings are worth drawing out before the classification:</p>
@@ -48098,7 +47171,7 @@ A proposal to cover the read-side (query) is in 
preparation.
 <div class="content">
 <img 
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnIHN0YW5kYWxvbmU9J25vJz8+PHN2ZyBjb250ZW50U3R5bGVUeXBlPSd0ZXh0L2NzcycgaGVpZ2h0PSc1NzdweCcgcHJlc2VydmVBc3BlY3RSYXRpbz0nbm9uZScgc3R5bGU9J3dpZHRoOjEzOTFweDtoZWlnaHQ6NTc3cHg7YmFja2dyb3VuZDojRkZGRkZGOycgdmVyc2lvbj0nMS4xJyB2aWV3Qm94PScwIDAgMTM5MSA1NzcnIHdpZHRoPScxMzkxcHgnIHhtbG5zOnhsaW5rPSdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHpvb21BbmRQYW49J21hZ25pZnknPjxkZWZzLz48
 [...]
 </div>
-<div class="title">Figure 4. Command Processing Architecture Level 1 - 
Core</div>
+<div class="title">Figure 7. Command Processing Architecture Level 1 - 
Core</div>
 </div>
 </div>
 </div>
@@ -48116,7 +47189,7 @@ A proposal to cover the read-side (query) is in 
preparation.
 <div class="content">
 <img 
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnIHN0YW5kYWxvbmU9J25vJz8+PHN2ZyBjb250ZW50U3R5bGVUeXBlPSd0ZXh0L2NzcycgaGVpZ2h0PSc1NDBweCcgcHJlc2VydmVBc3BlY3RSYXRpbz0nbm9uZScgc3R5bGU9J3dpZHRoOjk5M3B4O2hlaWdodDo1NDBweDtiYWNrZ3JvdW5kOiNGRkZGRkY7JyB2ZXJzaW9uPScxLjEnIHZpZXdCb3g9JzAgMCA5OTMgNTQwJyB3aWR0aD0nOTkzcHgnIHhtbG5zOnhsaW5rPSdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHpvb21BbmRQYW49J21hZ25pZnknPjxkZWZzLz48Zz48
 [...]
 </div>
-<div class="title">Figure 5. Command Processing Architecture Level 2 - 
Dispatcher</div>
+<div class="title">Figure 8. Command Processing Architecture Level 2 - 
Dispatcher</div>
 </div>
 <div class="listingblock">
 <div class="title">Command Dispatcher</div>
@@ -48202,7 +47275,7 @@ Early phase of development. No prototype available yet, 
but most likely module <
 <div class="content">
 <img 
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnIHN0YW5kYWxvbmU9J25vJz8+PHN2ZyBjb250ZW50U3R5bGVUeXBlPSd0ZXh0L2NzcycgaGVpZ2h0PScyOTBweCcgcHJlc2VydmVBc3BlY3RSYXRpbz0nbm9uZScgc3R5bGU9J3dpZHRoOjEyMTVweDtoZWlnaHQ6MjkwcHg7YmFja2dyb3VuZDojRkZGRkZGOycgdmVyc2lvbj0nMS4xJyB2aWV3Qm94PScwIDAgMTIxNSAyOTAnIHdpZHRoPScxMjE1cHgnIHhtbG5zOnhsaW5rPSdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHpvb21BbmRQYW49J21hZ25pZnknPjxkZWZzLz48
 [...]
 </div>
-<div class="title">Figure 6. Command Processing Architecture Level 2 - 
Handlers</div>
+<div class="title">Figure 9. Command Processing Architecture Level 2 - 
Handlers</div>
 </div>
 <div class="sect3">
 <h4 id="_handler_manager"><a class="link" href="#_handler_manager">Handler 
Manager</a></h4>
@@ -48258,7 +47331,7 @@ Early phase of development. No prototype available yet, 
but most likely module <
 <div class="content">
 <img 
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnIHN0YW5kYWxvbmU9J25vJz8+PHN2ZyBjb250ZW50U3R5bGVUeXBlPSd0ZXh0L2NzcycgaGVpZ2h0PSc3MDhweCcgcHJlc2VydmVBc3BlY3RSYXRpbz0nbm9uZScgc3R5bGU9J3dpZHRoOjEyODRweDtoZWlnaHQ6NzA4cHg7YmFja2dyb3VuZDojRkZGRkZGOycgdmVyc2lvbj0nMS4xJyB2aWV3Qm94PScwIDAgMTI4NCA3MDgnIHdpZHRoPScxMjg0cHgnIHhtbG5zOnhsaW5rPSdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHpvb21BbmRQYW49J21hZ25pZnknPjxkZWZzLz48
 [...]
 </div>
-<div class="title">Figure 7. Command Processing Architecture Level 2 - 
Hooks</div>
+<div class="title">Figure 10. Command Processing Architecture Level 2 - 
Hooks</div>
 </div>
 <div class="sect3">
 <h4 id="_hook_manager"><a class="link" href="#_hook_manager">Hook 
Manager</a></h4>
@@ -48342,7 +47415,7 @@ Early phase of development. No prototype available yet, 
but most likely module <
 <div class="content">
 <img 
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnIHN0YW5kYWxvbmU9J25vJz8+PHN2ZyBjb250ZW50U3R5bGVUeXBlPSd0ZXh0L2NzcycgaGVpZ2h0PSc2MzJweCcgcHJlc2VydmVBc3BlY3RSYXRpbz0nbm9uZScgc3R5bGU9J3dpZHRoOjkxMnB4O2hlaWdodDo2MzJweDtiYWNrZ3JvdW5kOiNGRkZGRkY7JyB2ZXJzaW9uPScxLjEnIHZpZXdCb3g9JzAgMCA5MTIgNjMyJyB3aWR0aD0nOTEycHgnIHhtbG5zOnhsaW5rPSdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHpvb21BbmRQYW49J21hZ25pZnknPjxkZWZzLz48Zz48
 [...]
 </div>
-<div class="title">Figure 8. Command Processing Architecture Level 2 - 
Store</div>
+<div class="title">Figure 11. Command Processing Architecture Level 2 - 
Store</div>
 </div>
 </div>
 <div class="sect2">
@@ -60188,7 +59261,7 @@ a loan that is not charged off.</p>
 <div class="content">
 <img 
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnIHN0YW5kYWxvbmU9J25vJz8+PHN2ZyBjb250ZW50U3R5bGVUeXBlPSd0ZXh0L2NzcycgaGVpZ2h0PSczOTBweCcgcHJlc2VydmVBc3BlY3RSYXRpbz0nbm9uZScgc3R5bGU9J3dpZHRoOjk4NXB4O2hlaWdodDozOTBweDtiYWNrZ3JvdW5kOiNGRkZGRkY7JyB2ZXJzaW9uPScxLjEnIHZpZXdCb3g9JzAgMCA5ODUgMzkwJyB3aWR0aD0nOTg1cHgnIHhtbG5zOnhsaW5rPSdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHpvb21BbmRQYW49J21hZ25pZnknPjxkZWZzLz48Zz48
 [...]
 </div>
-<div class="title">Figure 9. Charge-off request/response flow</div>
+<div class="title">Figure 12. Charge-off request/response flow</div>
 </div>
 </div>
 </div>
@@ -61337,10 +60410,10 @@ which admit <code>CLOSED_WRITTEN_OFF</code>:</p>
 <div class="ulist">
 <ul>
 <li>
-<p><code>WorkingCapitalLoanWrittenOffBusinessEvent</code> — emitted after a 
write-off.</p>
+<p><code>WorkingCapitalLoanWriteOffTransactionBusinessEvent</code> — emitted 
after a write-off.</p>
 </li>
 <li>
-<p><code>WorkingCapitalLoanUndoWrittenOffBusinessEvent</code> — emitted after 
an undo write-off.</p>
+<p><code>WorkingCapitalLoanUndoWriteOffTransactionBusinessEvent</code> — 
emitted after an undo write-off.</p>
 </li>
 <li>
 <p><code>WorkingCapitalLoanBalanceChangedBusinessEvent</code> — emitted by 
both operations, since the write-off zeroes the<br>
@@ -66157,7 +65230,7 @@ inserts the dedicated permission.</p>
 <div class="content">
 <img 
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnIHN0YW5kYWxvbmU9J25vJz8+PHN2ZyBjb250ZW50U3R5bGVUeXBlPSd0ZXh0L2NzcycgaGVpZ2h0PSczNTRweCcgcHJlc2VydmVBc3BlY3RSYXRpbz0nbm9uZScgc3R5bGU9J3dpZHRoOjcwMXB4O2hlaWdodDozNTRweDtiYWNrZ3JvdW5kOiNGRkZGRkY7JyB2ZXJzaW9uPScxLjEnIHZpZXdCb3g9JzAgMCA3MDEgMzU0JyB3aWR0aD0nNzAxcHgnIHhtbG5zOnhsaW5rPSdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHpvb21BbmRQYW49J21hZ25pZnknPjxkZWZzLz48Zz48
 [...]
 </div>
-<div class="title">Figure 10. Entity-Relationship — action tables reused by 
Disable / Enable</div>
+<div class="title">Figure 13. Entity-Relationship — action tables reused by 
Disable / Enable</div>
 </div>
 <div class="sect4">
 <h5 id="_m_wc_loan_delinquency_action"><a class="link" 
href="#_m_wc_loan_delinquency_action"><code>m_wc_loan_delinquency_action</code></a></h5>
@@ -66704,7 +65777,7 @@ side effect (delinquency lifts the active 
classification; breach only persists t
 <div class="content">
 <img 
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnIHN0YW5kYWxvbmU9J25vJz8+PHN2ZyBjb250ZW50U3R5bGVUeXBlPSd0ZXh0L2NzcycgaGVpZ2h0PSc3NTZweCcgcHJlc2VydmVBc3BlY3RSYXRpbz0nbm9uZScgc3R5bGU9J3dpZHRoOjE0OThweDtoZWlnaHQ6NzU2cHg7YmFja2dyb3VuZDojRkZGRkZGOycgdmVyc2lvbj0nMS4xJyB2aWV3Qm94PScwIDAgMTQ5OCA3NTYnIHdpZHRoPScxNDk4cHgnIHhtbG5zOnhsaW5rPSdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHpvb21BbmRQYW49J21hZ25pZnknPjxkZWZzLz48
 [...]
 </div>
-<div class="title">Figure 11. Disable — request to persistence</div>
+<div class="title">Figure 14. Disable — request to persistence</div>
 </div>
 </div>
 <div class="sect4">
@@ -66718,7 +65791,7 @@ recomputes its own schedule.</p>
 <div class="content">
 <img 
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnIHN0YW5kYWxvbmU9J25vJz8+PHN2ZyBjb250ZW50U3R5bGVUeXBlPSd0ZXh0L2NzcycgaGVpZ2h0PSc3MjFweCcgcHJlc2VydmVBc3BlY3RSYXRpbz0nbm9uZScgc3R5bGU9J3dpZHRoOjE1MDRweDtoZWlnaHQ6NzIxcHg7YmFja2dyb3VuZDojRkZGRkZGOycgdmVyc2lvbj0nMS4xJyB2aWV3Qm94PScwIDAgMTUwNCA3MjEnIHdpZHRoPScxNTA0cHgnIHhtbG5zOnhsaW5rPSdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHpvb21BbmRQYW49J21hZ25pZnknPjxkZWZzLz48
 [...]
 </div>
-<div class="title">Figure 12. Enable — closing the window and recomputing</div>
+<div class="title">Figure 15. Enable — closing the window and recomputing</div>
 </div>
 </div>
 <div class="sect4">
@@ -66730,7 +65803,7 @@ recomputes its own schedule.</p>
 <div class="content">
 <img 
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnIHN0YW5kYWxvbmU9J25vJz8+PHN2ZyBjb250ZW50U3R5bGVUeXBlPSd0ZXh0L2NzcycgaGVpZ2h0PScxMzE5cHgnIHByZXNlcnZlQXNwZWN0UmF0aW89J25vbmUnIHN0eWxlPSd3aWR0aDo5MzVweDtoZWlnaHQ6MTMxOXB4O2JhY2tncm91bmQ6I0ZGRkZGRjsnIHZlcnNpb249JzEuMScgdmlld0JveD0nMCAwIDkzNSAxMzE5JyB3aWR0aD0nOTM1cHgnIHhtbG5zOnhsaW5rPSdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHpvb21BbmRQYW49J21hZ25pZnknPjxkZWZzLz48
 [...]
 </div>
-<div class="title">Figure 13. Disable / Enable validation gate</div>
+<div class="title">Figure 16. Disable / Enable validation gate</div>
 </div>
 </div>
 </div>
@@ -75576,19 +74649,19 @@ Have a look at the specifications in 
<code>fineract-provider</code> for an initi
 <p><strong>Location</strong>: 
<code>integration-tests/src/test/java/org/apache/fineract/integrationtests</code></p>
 </li>
 <li>
-<p><strong>Framework</strong>: JUnit 5 with REST Assured for HTTP 
communication</p>
+<p><strong>Framework</strong>: JUnit 5. Tests under <code>client/feign</code> 
drive the server through the generated Feign client; the remainder still use 
REST Assured for HTTP communication</p>
 </li>
 <li>
-<p><strong>Base Class</strong>: Most tests extend 
<code>BaseLoanIntegrationTest</code> or <code>IntegrationTest</code></p>
-</li>
-<li>
-<p><strong>Client Library</strong>: Uses Fineract client library for type-safe 
API interactions</p>
+<p><strong>Base Class</strong>: Most tests extend 
<code>BaseLoanIntegrationTest</code> or <code>IntegrationTest</code>; Feign 
tests extend <code>FeignLoanTestBase</code> or 
<code>FeignSavingsTestBase</code></p>
 </li>
 <li>
 <p><strong>Prerequisites</strong>: Running Apache Fineract instance (default: 
<a href="https://localhost:8443"; class="bare">localhost:8443</a>)</p>
 </li>
 </ul>
 </div>
+<div class="paragraph">
+<p>New tests should be written against the generated Feign client, which is 
covered in <a href="#_feign_integration_tests">Feign Integration Tests</a>. The 
rest of this chapter describes the setup shared by both, and the REST Assured 
pattern used by the tests that have not yet been migrated.</p>
+</div>
 </div>
 <div class="sect4">
 <h5 id="_key_characteristics_3"><a class="link" 
href="#_key_characteristics_3">Key Characteristics</a></h5>
@@ -75629,18 +74702,32 @@ Have a look at the specifications in 
<code>fineract-provider</code> for an initi
 <div class="paragraph">
 <p>Before running integration tests, ensure the databases are created:</p>
 </div>
+<div class="paragraph">
+<p>There is a separate task per database engine: <code>createPGDB</code> for 
PostgreSQL, <code>createDB</code> for MariaDB, and <code>createMySQLDB</code> 
for MySQL.</p>
+</div>
 <div class="listingblock">
 <div class="content">
-<pre class="rouge highlight"><code data-lang="bash"><span class="c"># Create 
required databases</span>
-./gradlew createDB <span class="nt">-PdbName</span><span 
class="o">=</span>fineract_tenants
-./gradlew createDB <span class="nt">-PdbName</span><span 
class="o">=</span>fineract_default</code></pre>
+<pre class="rouge highlight"><code data-lang="bash"><span class="c"># Create 
required databases (PostgreSQL)</span>
+./gradlew createPGDB <span class="nt">-PdbName</span><span 
class="o">=</span>fineract_tenants
+./gradlew createPGDB <span class="nt">-PdbName</span><span 
class="o">=</span>fineract_default</code></pre>
 </div>
 </div>
 </div>
 <div class="sect4">
 <h5 id="_fineract_instance"><a class="link" 
href="#_fineract_instance">Fineract Instance</a></h5>
 <div class="paragraph">
-<p>Integration tests run fineract instance from cargo plugin by default.</p>
+<p>Integration tests run fineract instance from cargo plugin by default. The 
<code>test</code> task starts a Tomcat container, deploys 
<code>fineract-provider.war</code> into it, waits for the instance to come up, 
and stops it afterwards.</p>
+</div>
+<div class="paragraph">
+<p>Pass <code>-PcargoDisabled</code> to skip all of that and run against an 
instance you started yourself:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="rouge highlight"><code data-lang="bash">./gradlew 
:integration-tests:test <span class="nt">-PcargoDisabled</span> <span 
class="nt">--tests</span> ClientLoanIntegrationTest</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Without it, the suite runs against the Cargo-managed container regardless 
of any instance already listening on the configured port.</p>
 </div>
 </div>
 </div>
@@ -75770,27 +74857,25 @@ curl <span class="nt">-k</span> 
https://localhost:8443/actuator/health</code></p
 <h5 id="_advanced_test_execution"><a class="link" 
href="#_advanced_test_execution">Advanced Test Execution</a></h5>
 <div class="sect5">
 <h6 id="_run_with_test_filtering"><a class="link" 
href="#_run_with_test_filtering">Run with Test Filtering</a></h6>
+<div class="paragraph">
+<p><code>--tests</code> is the only filter the <code>test</code> task accepts. 
Repeat it to select several patterns; there is no corresponding exclude 
option.</p>
+</div>
 <div class="listingblock">
 <div class="content">
-<pre class="rouge highlight"><code data-lang="bash"><span class="c"># Run 
tests excluding specific packages</span>
-./gradlew :integration-tests:test <span class="nt">--tests</span> <span 
class="s2">"*"</span> <span class="se">\</span>
-  <span class="nt">--exclude</span> <span class="s2">"*Deprecated*"</span>
-
-<span class="c"># Run only fast tests (custom tag)</span>
-./gradlew :integration-tests:test <span class="nt">--tests</span> <span 
class="s2">"*Fast*"</span></code></pre>
+<pre class="rouge highlight"><code data-lang="bash"><span class="c"># Select 
several patterns</span>
+./gradlew :integration-tests:test <span class="se">\</span>
+  <span class="nt">--tests</span> <span class="s2">"*Loan*"</span> <span 
class="se">\</span>
+  <span class="nt">--tests</span> <span 
class="s2">"*Savings*"</span></code></pre>
 </div>
 </div>
+<div class="paragraph">
+<p>To skip a test permanently, annotate it with <code>@Disabled</code> rather 
than filtering it on the command line.</p>
+</div>
 </div>
 <div class="sect5">
 <h6 id="_parallel_execution"><a class="link" 
href="#_parallel_execution">Parallel Execution</a></h6>
-<div class="listingblock">
-<div class="content">
-<pre class="rouge highlight"><code data-lang="bash"><span class="c"># Run 
tests in parallel</span>
-./gradlew :integration-tests:test <span class="nt">--parallel</span> <span 
class="nt">--max-workers</span><span class="o">=</span>4
-
-<span class="c"># Set custom thread count</span>
-./gradlew :integration-tests:test <span class="nt">--parallel</span> <span 
class="nt">--max-workers</span><span class="o">=</span>8</code></pre>
-</div>
+<div class="paragraph">
+<p><code>--max-workers</code> limits Gradle&#8217;s own worker count; it does 
not fork the test JVM. Within-task parallelism is controlled by 
<code>maxParallelForks</code> on the <code>test</code> task, which 
<code>integration-tests</code> does not set — the suite runs in a single 
JVM.</p>
 </div>
 <div class="admonitionblock warning">
 <table>
@@ -75799,7 +74884,7 @@ curl <span class="nt">-k</span> 
https://localhost:8443/actuator/health</code></p
 <i class="fa icon-warning" title="Warning"></i>
 </td>
 <td class="content">
-Some integration tests may have dependencies on shared state. Use parallel 
execution carefully and ensure tests are properly isolated.
+Integration tests share the database of the instance under test. Raising 
<code>maxParallelForks</code> requires the tests to be isolated from one 
another first.
 </td>
 </tr>
 </table>
@@ -75809,13 +74894,25 @@ Some integration tests may have dependencies on 
shared state. Use parallel execu
 <h6 id="_run_with_custom_jvm_arguments"><a class="link" 
href="#_run_with_custom_jvm_arguments">Run with Custom JVM Arguments</a></h6>
 <div class="listingblock">
 <div class="content">
-<pre class="rouge highlight"><code data-lang="bash"><span class="c"># Increase 
heap size for large test suites</span>
-./gradlew :integration-tests:test <span class="nt">-Xmx4g</span>
-
-<span class="c"># Enable debugging</span>
+<pre class="rouge highlight"><code data-lang="bash"><span class="c"># Attach a 
debugger to the test JVM (suspends until the debugger connects)</span>
 ./gradlew :integration-tests:test <span 
class="nt">--debug-jvm</span></code></pre>
 </div>
 </div>
+<div class="paragraph">
+<p>The Gradle daemon&#8217;s own heap is set by 
<code>org.gradle.jvmargs</code> in <code>gradle.properties</code>.</p>
+</div>
+<div class="admonitionblock caution">
+<table>
+<tr>
+<td class="icon">
+<i class="fa icon-caution" title="Caution"></i>
+</td>
+<td class="content">
+Overriding it on the command line with <code>-Dorg.gradle.jvmargs</code> 
replaces the whole value, including the <code>--add-exports</code> and 
<code>--add-opens</code> flags the build needs. Repeat those flags if you 
override it.
+</td>
+</tr>
+</table>
+</div>
 </div>
 <div class="sect5">
 <h6 id="_generate_test_reports"><a class="link" 
href="#_generate_test_reports">Generate Test Reports</a></h6>
@@ -75894,19 +74991,21 @@ Some integration tests may have dependencies on 
shared state. Use parallel execu
 <div class="sect3">
 <h4 id="_test_structure"><a class="link" href="#_test_structure">Test 
Structure</a></h4>
 <div class="sect4">
-<h5 id="_baseloanintegrationtest_overview"><a class="link" 
href="#_baseloanintegrationtest_overview">BaseLoanIntegrationTest 
Overview</a></h5>
+<h5 id="_loan_test_base_class_overview"><a class="link" 
href="#_loan_test_base_class_overview">Loan Test Base Class Overview</a></h5>
 <div class="paragraph">
-<p><code>BaseLoanIntegrationTest</code> is the comprehensive base test class 
for loan-related integration tests. It provides:</p>
+<p><code>FeignLoanTestBase</code> is the base class for loan-related 
integration tests, and provides the helpers described below. 
<code>BaseLoanIntegrationTest</code> offers the same surface for the REST 
Assured tests that have not yet been migrated.</p>
 </div>
 <div class="sect5">
 <h6 id="_pre_configured_loan_product_creation"><a class="link" 
href="#_pre_configured_loan_product_creation">Pre-configured Loan Product 
Creation</a></h6>
+<div class="paragraph">
+<p>These methods return a <code>PostLoanProductsRequest</code> describing the 
product. Pass it to <code>createLoanProduct</code> to create the product and 
obtain its identifier.</p>
+</div>
 <div class="listingblock">
 <div class="content">
-<pre class="rouge highlight"><code data-lang="java"><span class="c1">// Create 
standard loan products</span>
-<span 
class="n">createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct</span><span 
class="o">()</span>
-<span class="n">create4IProgressive</span><span class="o">()</span> <span 
class="c1">// Progressive loan products</span>
-<span class="n">create4IProgressiveWithCapitalizedIncome</span><span 
class="o">()</span> <span class="c1">// With capitalized income</span>
-<span 
class="n">createOnePeriod30DaysPeriodicAccrualProductWithAdvancedPaymentAllocation</span><span
 class="o">()</span></code></pre>
+<pre class="rouge highlight"><code data-lang="java"><span 
class="n">createOnePeriod30DaysLongNoInterestPeriodicAccrualProduct</span><span 
class="o">()</span>
+<span class="n">create4IProgressive</span><span class="o">()</span>
+<span class="n">create4IProgressiveWithCapitalizedIncome</span><span 
class="o">()</span>
+<span 
class="n">createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation</span><span
 class="o">()</span></code></pre>
 </div>
 </div>
 </div>
@@ -75994,6 +75093,12 @@ Some integration tests may have dependencies on shared 
state. Use parallel execu
 <h5 id="_common_test_patterns"><a class="link" 
href="#_common_test_patterns">Common Test Patterns</a></h5>
 <div class="sect5">
 <h6 id="_test_setup_pattern"><a class="link" href="#_test_setup_pattern">Test 
Setup Pattern</a></h6>
+<div class="paragraph">
+<p>A test extending <code>FeignLoanTestBase</code> needs no setup block; the 
base class builds the client and its helpers.</p>
+</div>
+<div class="paragraph">
+<p>The REST Assured tests that have not yet been migrated build a request 
specification per test:</p>
+</div>
 <div class="listingblock">
 <div class="content">
 <pre class="rouge highlight"><code data-lang="java"><span 
class="nd">@BeforeEach</span>
@@ -76010,6 +75115,9 @@ Some integration tests may have dependencies on shared 
state. Use parallel execu
 <span class="o">}</span></code></pre>
 </div>
 </div>
+<div class="paragraph">
+<p>Do not copy this into a new test. 
<code>loginIntoServerAndGetBase64EncodedAuthenticationKey</code> authenticates 
against the server on every test method.</p>
+</div>
 </div>
 <div class="sect5">
 <h6 id="_date_specific_operations"><a class="link" 
href="#_date_specific_operations">Date-Specific Operations</a></h6>
@@ -76058,19 +75166,15 @@ Some integration tests may have dependencies on 
shared state. Use parallel execu
 </div>
 <div class="sect5">
 <h6 id="_progressive_loan_testing"><a class="link" 
href="#_progressive_loan_testing">Progressive Loan Testing</a></h6>
+<div class="paragraph">
+<p>The <code>create*</code> methods return a 
<code>PostLoanProductsRequest</code> describing the product, not a product 
identifier. Pass the request to <code>createLoanProduct</code> to obtain the 
identifier, customising it first if the test needs to.</p>
+</div>
 <div class="listingblock">
 <div class="content">
-<pre class="rouge highlight"><code data-lang="java"><span class="c1">// Create 
progressive loan product</span>
-<span class="nc">Long</span> <span class="n">productId</span> <span 
class="o">=</span> <span class="n">create4IProgressive</span><span 
class="o">();</span>
-
-<span class="c1">// Apply and approve</span>
-<span class="nc">Long</span> <span class="n">loanId</span> <span 
class="o">=</span> <span class="n">applyAndApproveProgressiveLoan</span><span 
class="o">(</span><span class="n">clientId</span><span class="o">,</span> <span 
class="n">productId</span><span class="o">,</span>
-    <span class="n">amount</span><span class="o">,</span> <span 
class="n">numberOfRepayments</span><span class="o">,</span> <span 
class="n">interestRate</span><span class="o">);</span>
+<pre class="rouge highlight"><code data-lang="java"><span 
class="nc">Long</span> <span class="n">productId</span> <span 
class="o">=</span> <span class="n">createLoanProduct</span><span 
class="o">(</span><span class="n">create4IProgressive</span><span 
class="o">());</span>
 
-<span class="c1">// Test advanced features</span>
-<span class="n">testCapitalizedIncome</span><span class="o">(</span><span 
class="n">loanId</span><span class="o">);</span>
-<span class="n">testDownPayment</span><span class="o">(</span><span 
class="n">loanId</span><span class="o">);</span>
-<span class="n">testAdvancedPaymentAllocation</span><span 
class="o">(</span><span class="n">loanId</span><span 
class="o">);</span></code></pre>
+<span class="nc">Long</span> <span class="n">loanId</span> <span 
class="o">=</span> <span class="n">applyAndApproveProgressiveLoan</span><span 
class="o">(</span><span class="n">clientId</span><span class="o">,</span> <span 
class="n">productId</span><span class="o">,</span> <span class="s">"01 January 
2024"</span><span class="o">,</span>
+    <span class="mf">1000.0</span><span class="o">,</span> <span 
class="mf">10.0</span><span class="o">,</span> <span class="mi">12</span><span 
class="o">,</span> <span class="n">request</span> <span class="o">-&gt;</span> 
<span class="n">request</span><span class="o">.</span><span 
class="na">setLoanTermFrequency</span><span class="o">(</span><span 
class="mi">12</span><span class="o">));</span></code></pre>
 </div>
 </div>
 </div>
@@ -76083,30 +75187,16 @@ Some integration tests may have dependencies on 
shared state. Use parallel execu
 <div class="sect5">
 <h6 id="_1_create_test_class"><a class="link" href="#_1_create_test_class">1. 
Create Test Class</a></h6>
 <div class="paragraph">
-<p>Create a new test class in 
<code>integration-tests/src/test/java/org/apache/fineract/integrationtests/</code>:</p>
+<p>Create a new test class in 
<code>integration-tests/src/test/java/org/apache/fineract/integrationtests/</code>
 and extend <code>FeignLoanTestBase</code>:</p>
 </div>
 <div class="listingblock">
 <div class="content">
 <pre class="rouge highlight"><code data-lang="java"><span 
class="kn">package</span> <span 
class="nn">org.apache.fineract.integrationtests</span><span class="o">;</span>
 
-<span class="kn">import</span> <span 
class="nn">org.apache.fineract.integrationtests.common.Utils</span><span 
class="o">;</span>
+<span class="kn">import</span> <span 
class="nn">org.apache.fineract.integrationtests.client.feign.FeignLoanTestBase</span><span
 class="o">;</span>
 <span class="kn">import</span> <span 
class="nn">org.junit.jupiter.api.Test</span><span class="o">;</span>
-<span class="kn">import</span> <span 
class="nn">org.junit.jupiter.api.BeforeEach</span><span class="o">;</span>
 
-<span class="kd">public</span> <span class="kd">class</span> <span 
class="nc">MyNewFeatureIntegrationTest</span> <span class="kd">extends</span> 
<span class="nc">BaseLoanIntegrationTest</span> <span class="o">{</span>
-
-    <span class="nd">@BeforeEach</span>
-    <span class="kd">public</span> <span class="kt">void</span> <span 
class="nf">setup</span><span class="o">()</span> <span class="o">{</span>
-        <span class="nc">Utils</span><span class="o">.</span><span 
class="na">initializeRESTAssured</span><span class="o">();</span>
-        <span class="k">this</span><span class="o">.</span><span 
class="na">requestSpec</span> <span class="o">=</span> <span 
class="k">new</span> <span class="nc">RequestSpecBuilder</span><span 
class="o">()</span>
-            <span class="o">.</span><span 
class="na">setContentType</span><span class="o">(</span><span 
class="nc">ContentType</span><span class="o">.</span><span 
class="na">JSON</span><span class="o">)</span>
-            <span class="o">.</span><span class="na">build</span><span 
class="o">();</span>
-        <span class="k">this</span><span class="o">.</span><span 
class="na">requestSpec</span><span class="o">.</span><span 
class="na">header</span><span class="o">(</span><span 
class="s">"Authorization"</span><span class="o">,</span> <span class="s">"Basic 
"</span> <span class="o">+</span>
-            <span class="nc">Utils</span><span class="o">.</span><span 
class="na">loginIntoServerAndGetBase64EncodedAuthenticationKey</span><span 
class="o">());</span>
-        <span class="k">this</span><span class="o">.</span><span 
class="na">responseSpec</span> <span class="o">=</span> <span 
class="k">new</span> <span class="nc">ResponseSpecBuilder</span><span 
class="o">()</span>
-            <span class="o">.</span><span 
class="na">expectStatusCode</span><span class="o">(</span><span 
class="mi">200</span><span class="o">)</span>
-            <span class="o">.</span><span class="na">build</span><span 
class="o">();</span>
-    <span class="o">}</span>
+<span class="kd">public</span> <span class="kd">class</span> <span 
class="nc">MyNewFeatureIntegrationTest</span> <span class="kd">extends</span> 
<span class="nc">FeignLoanTestBase</span> <span class="o">{</span>
 
     <span class="nd">@Test</span>
     <span class="kd">public</span> <span class="kt">void</span> <span 
class="nf">testMyNewFeature</span><span class="o">()</span> <span 
class="o">{</span>
@@ -76115,33 +75205,32 @@ Some integration tests may have dependencies on 
shared state. Use parallel execu
 <span class="o">}</span></code></pre>
 </div>
 </div>
+<div class="paragraph">
+<p>The conventions that apply to the body of such a test — how to call the 
API, how to assert failures, and what to do when a generated model is missing a 
field — are covered in <a href="#_feign_integration_tests">Feign Integration 
Tests</a>.</p>
+</div>
 </div>
 <div class="sect5">
 <h6 id="_2_use_helper_classes"><a class="link" 
href="#_2_use_helper_classes">2. Use Helper Classes</a></h6>
 <div class="paragraph">
-<p>Leverage existing helper classes:</p>
+<p><code>FeignLoanTestBase</code> exposes the helpers as fields, already 
constructed against the shared client. There is no request specification to 
pass around:</p>
 </div>
 <div class="listingblock">
 <div class="content">
-<pre class="rouge highlight"><code data-lang="java"><span class="c1">// Client 
operations</span>
-<span class="nc">ClientHelper</span> <span class="n">clientHelper</span> <span 
class="o">=</span> <span class="k">new</span> <span 
class="nc">ClientHelper</span><span class="o">(</span><span 
class="n">requestSpec</span><span class="o">,</span> <span 
class="n">responseSpec</span><span class="o">);</span>
-<span class="nc">Long</span> <span class="n">clientId</span> <span 
class="o">=</span> <span class="n">clientHelper</span><span 
class="o">.</span><span class="na">createClient</span><span 
class="o">(...);</span>
-
-<span class="c1">// Loan operations</span>
-<span class="nc">LoanTransactionHelper</span> <span 
class="n">loanHelper</span> <span class="o">=</span> <span class="k">new</span> 
<span class="nc">LoanTransactionHelper</span><span class="o">(</span><span 
class="n">requestSpec</span><span class="o">,</span> <span 
class="n">responseSpec</span><span class="o">);</span>
-<span class="nc">Long</span> <span class="n">loanId</span> <span 
class="o">=</span> <span class="n">loanHelper</span><span 
class="o">.</span><span class="na">applyLoan</span><span class="o">(...);</span>
-
-<span class="c1">// Account operations</span>
-<span class="nc">AccountHelper</span> <span class="n">accountHelper</span> 
<span class="o">=</span> <span class="k">new</span> <span 
class="nc">AccountHelper</span><span class="o">(</span><span 
class="n">requestSpec</span><span class="o">,</span> <span 
class="n">responseSpec</span><span class="o">);</span>
-
-<span class="c1">// Business date operations</span>
-<span class="nc">BusinessDateHelper</span> <span 
class="n">businessDateHelper</span> <span class="o">=</span> <span 
class="k">new</span> <span class="nc">BusinessDateHelper</span><span 
class="o">();</span>
-<span class="n">businessDateHelper</span><span class="o">.</span><span 
class="na">updateBusinessDate</span><span class="o">(...);</span>
-
-<span class="c1">// COB operations</span>
-<span class="nc">InlineLoanCOBHelper</span> <span class="n">cobHelper</span> 
<span class="o">=</span> <span class="k">new</span> <span 
class="nc">InlineLoanCOBHelper</span><span class="o">(</span><span 
class="n">requestSpec</span><span class="o">,</span> <span 
class="n">responseSpec</span><span class="o">);</span>
-<span class="n">cobHelper</span><span class="o">.</span><span 
class="na">executeInlineCOB</span><span class="o">(</span><span 
class="n">loanId</span><span class="o">);</span></code></pre>
+<pre class="rouge highlight"><code data-lang="java"><span 
class="nc">Long</span> <span class="n">clientId</span> <span class="o">=</span> 
<span class="n">clientHelper</span><span class="o">.</span><span 
class="na">createClient</span><span class="o">(...);</span>
+<span class="nc">PostLoansResponse</span> <span class="n">loan</span> <span 
class="o">=</span> <span class="n">loanHelper</span><span 
class="o">.</span><span class="na">applyForLoan</span><span 
class="o">(</span><span class="n">request</span><span class="o">);</span>
+<span class="n">businessDateHelper</span><span class="o">.</span><span 
class="na">updateBusinessDate</span><span class="o">(...);</span></code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Helpers that the base class does not expose are constructed with the 
client:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="rouge highlight"><code data-lang="java"><span 
class="nc">FeignOfficeHelper</span> <span class="n">officeHelper</span> <span 
class="o">=</span> <span class="k">new</span> <span 
class="nc">FeignOfficeHelper</span><span class="o">(</span><span 
class="nc">FineractFeignClientHelper</span><span class="o">.</span><span 
class="na">getFineractFeignClient</span><span class="o">());</span></code></pre>
+</div>
 </div>
+<div class="paragraph">
+<p>The full set lives in 
<code>integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign/helpers</code>.</p>
 </div>
 </div>
 <div class="sect5">
@@ -76254,12 +75343,23 @@ netstat <span class="nt">-tulpn</span> | <span 
class="nb">grep </span>8443
 
 <span class="c"># Check Fineract logs (logs go to console/stdout)</span>
 <span class="c"># If running in background with output redirection:</span>
-<span class="c"># tail -f build/bootRun.log</span>
-
-<span class="c"># Restart Fineract if needed</span>
-pkill <span class="nt">-f</span> bootRun
-./gradlew bootRun &amp;</code></pre>
+<span class="c"># tail -f build/bootRun.log</span></code></pre>
+</div>
+</div>
+<div class="admonitionblock caution">
+<table>
+<tr>
+<td class="icon">
+<i class="fa icon-caution" title="Caution"></i>
+</td>
+<td class="content">
+Do not stop the instance with <code>pkill -f bootRun</code> or by 
pattern-matching Gradle daemons. The server is a child of the daemon that 
launched it, so those patterns take the daemon down with it. Stop it from the 
terminal running it.
+</td>
+</tr>
+</table>
 </div>
+<div class="paragraph">
+<p>Rebuilding while an instance is running replaces the classes it loaded from 
<code>build/</code>. Reads may continue to work while writes start failing. 
Build first, then start the server.</p>
 </div>
 </div>
 <div class="sect5">
@@ -76272,21 +75372,20 @@ pkill <span class="nt">-f</span> bootRun
 </div>
 <div class="listingblock">
 <div class="content">
-<pre class="rouge highlight"><code data-lang="bash"><span class="c"># Check 
default credentials</span>
-mysql <span class="nt">-u</span> root <span class="nt">-pmysql</span> 
fineract_default <span class="nt">-e</span> <span class="se">\</span>
-  <span class="s2">"SELECT username, password FROM m_appuser WHERE username = 
'mifos';"</span>
-
-<span class="c"># Reset credentials if needed</span>
-mysql <span class="nt">-u</span> root <span class="nt">-pmysql</span> 
fineract_default <span class="nt">-e</span> <span class="se">\</span>
-  <span class="s2">"UPDATE m_appuser SET password = 
'5jdQ3dNQXHPzCuBbZVdQZ2XnVlPc3l2l' </span><span class="se">\</span><span 
class="s2">
-   WHERE username = 'mifos';"</span>
+<pre class="rouge highlight"><code data-lang="bash"><span class="c"># Check 
the user exists and is not locked</span>
+psql <span class="nt">-U</span> root <span class="nt">-d</span> 
fineract_default <span class="se">\</span>
+  <span class="nt">-c</span> <span class="s2">"SELECT username, nonlocked FROM 
m_appuser WHERE username = 'mifos';"</span>
 
 <span class="c"># Verify connection settings</span>
 <span class="nb">echo</span> <span class="s2">"Protocol: </span><span 
class="k">${</span><span class="nv">BACKEND_PROTOCOL</span><span 
class="k">:-</span><span class="nv">https</span><span class="k">}</span><span 
class="s2">"</span>
 <span class="nb">echo</span> <span class="s2">"Host: </span><span 
class="k">${</span><span class="nv">BACKEND_HOST</span><span 
class="k">:-</span><span class="nv">localhost</span><span 
class="k">}</span><span class="s2">"</span>
-<span class="nb">echo</span> <span class="s2">"Port: </span><span 
class="k">${</span><span class="nv">BACKEND_PORT</span><span 
class="k">:-</span><span class="nv">8443</span><span class="k">}</span><span 
class="s2">"</span></code></pre>
+<span class="nb">echo</span> <span class="s2">"Port: </span><span 
class="k">${</span><span class="nv">BACKEND_PORT</span><span 
class="k">:-</span><span class="nv">8443</span><span class="k">}</span><span 
class="s2">"</span>
+<span class="nb">echo</span> <span class="s2">"Tenant: </span><span 
class="k">${</span><span class="nv">BACKEND_TENANT</span><span 
class="k">:-</span><span class="nv">default</span><span class="k">}</span><span 
class="s2">"</span></code></pre>
 </div>
 </div>
+<div class="paragraph">
+<p>Check <code>BACKEND_TENANT</code> as well as the credentials. The tenant 
identifier is resolved before the user is authenticated, so an unknown tenant 
fails every test in the same way a bad password does.</p>
+</div>
 </div>
 <div class="sect5">
 <h6 id="_data_inconsistency"><a class="link" href="#_data_inconsistency">Data 
Inconsistency</a></h6>
@@ -76298,19 +75397,27 @@ mysql <span class="nt">-u</span> root <span 
class="nt">-pmysql</span> fineract_d
 </div>
 <div class="listingblock">
 <div class="content">
-<pre class="rouge highlight"><code data-lang="bash"><span class="c"># Reset 
database</span>
-mysql <span class="nt">-u</span> root <span class="nt">-pmysql</span> <span 
class="nt">-e</span> <span class="s2">"DROP DATABASE fineract_default;"</span>
-mysql <span class="nt">-u</span> root <span class="nt">-pmysql</span> <span 
class="nt">-e</span> <span class="s2">"DROP DATABASE fineract_tenants;"</span>
+<pre class="rouge highlight"><code data-lang="bash"><span class="c"># 1. Stop 
the running instance first: PostgreSQL refuses to drop a database</span>
+<span class="c">#    while the server still holds connections to it. Stop it 
from the</span>
+<span class="c">#    terminal it is running in.</span>
 
-<span class="c"># Recreate databases</span>
-./gradlew createDB <span class="nt">-PdbName</span><span 
class="o">=</span>fineract_tenants
-./gradlew createDB <span class="nt">-PdbName</span><span 
class="o">=</span>fineract_default
+<span class="c"># 2. Reset the databases</span>
+psql <span class="nt">-U</span> root <span class="nt">-d</span> postgres <span 
class="nt">-c</span> <span class="s2">"DROP DATABASE fineract_default;"</span>
+psql <span class="nt">-U</span> root <span class="nt">-d</span> postgres <span 
class="nt">-c</span> <span class="s2">"DROP DATABASE fineract_tenants;"</span>
+
+./gradlew createPGDB <span class="nt">-PdbName</span><span 
class="o">=</span>fineract_tenants
+./gradlew createPGDB <span class="nt">-PdbName</span><span 
class="o">=</span>fineract_default
 
-<span class="c"># Restart Fineract</span>
-pkill <span class="nt">-f</span> bootRun
-./gradlew bootRun &amp;</code></pre>
+<span class="c"># 3. Start Fineract again</span>
+./gradlew bootRun</code></pre>
 </div>
 </div>
+<div class="paragraph">
+<p><code>createPGDB</code> is the PostgreSQL task. <code>createDB</code> 
targets MariaDB and <code>createMySQLDB</code> targets MySQL; using the wrong 
one leaves the instance without the databases it expects.</p>
+</div>
+<div class="paragraph">
+<p>A long-lived development database is itself a source of failures. Tests 
that generate short random names begin to collide once the table holds enough 
rows, and tests that page through a listing stop finding what they created. A 
collision clears on a re-run; a paging miss does not. Reset the databases 
rather than re-running repeatedly.</p>
+</div>
 </div>
 <div class="sect5">
 <h6 id="_test_timeout"><a class="link" href="#_test_timeout">Test 
Timeout</a></h6>
@@ -76318,20 +75425,24 @@ pkill <span class="nt">-f</span> bootRun
 <p><strong>Symptom</strong>: Tests hang or timeout</p>
 </div>
 <div class="paragraph">
+<p>Timeouts are declared per test with JUnit&#8217;s <code>@Timeout</code>, or 
by the awaiting helper a test uses; there is no global timeout property.</p>
+</div>
+<div class="paragraph">
 <p><strong>Solutions</strong>:</p>
 </div>
 <div class="listingblock">
 <div class="content">
-<pre class="rouge highlight"><code data-lang="bash"><span class="c"># Increase 
test timeout</span>
-./gradlew :integration-tests:test <span class="nt">-Dtest</span>.timeout<span 
class="o">=</span>600
-
-<span class="c"># Check for database locks</span>
-mysql <span class="nt">-u</span> root <span class="nt">-pmysql</span> 
fineract_default <span class="nt">-e</span> <span class="s2">"SHOW 
PROCESSLIST;"</span>
+<pre class="rouge highlight"><code data-lang="bash"><span class="c"># Check 
for blocked queries</span>
+psql <span class="nt">-U</span> root <span class="nt">-d</span> 
fineract_default <span class="se">\</span>
+  <span class="nt">-c</span> <span class="s2">"SELECT pid, state, wait_event, 
query FROM pg_stat_activity WHERE state &lt;&gt; 'idle';"</span>
 
-<span class="c"># Kill long-running queries</span>
-mysql <span class="nt">-u</span> root <span class="nt">-pmysql</span> 
fineract_default <span class="nt">-e</span> <span class="s2">"KILL 
&lt;process_id&gt;;"</span></code></pre>
+<span class="c"># Cancel a long-running query</span>
+psql <span class="nt">-U</span> root <span class="nt">-d</span> 
fineract_default <span class="nt">-c</span> <span class="s2">"SELECT 
pg_cancel_backend(&lt;pid&gt;);"</span></code></pre>
 </div>
 </div>
+<div class="paragraph">
+<p>A test that waits on a scheduled job can exceed its budget simply because 
the job is slow on a database with accumulated data, rather than because of a 
regression. Re-run the class on its own before treating it as a failure.</p>
+</div>
 </div>
 <div class="sect5">
 <h6 id="_memory_issues"><a class="link" href="#_memory_issues">Memory 
Issues</a></h6>
@@ -76341,17 +75452,30 @@ mysql <span class="nt">-u</span> root <span 
class="nt">-pmysql</span> fineract_d
 <div class="paragraph">
 <p><strong>Solutions</strong>:</p>
 </div>
+<div class="paragraph">
+<p>The <code>test</code> task always forks its own JVM, and its heap comes 
from <code>maxHeapSize</code> on that task rather than from 
<code>org.gradle.jvmargs</code>. <code>integration-tests</code> does not set 
it, so the fork runs at Gradle&#8217;s default. Raise it in 
<code>integration-tests/build.gradle</code>:</p>
+</div>
 <div class="listingblock">
 <div class="content">
-<pre class="rouge highlight"><code data-lang="bash"><span class="c"># Increase 
heap size</span>
-./gradlew :integration-tests:test <span class="nt">-Xmx4g</span> <span 
class="nt">-Xms2g</span>
-
-<span class="c"># Run fewer tests in parallel</span>
-./gradlew :integration-tests:test <span class="nt">--max-workers</span><span 
class="o">=</span>2
-
-<span class="c"># Clean build directory</span>
-./gradlew clean</code></pre>
+<pre class="rouge highlight"><code data-lang="groovy"><span 
class="n">tasks</span><span class="o">.</span><span 
class="na">named</span><span class="o">(</span><span 
class="s1">'test'</span><span class="o">)</span> <span class="o">{</span>
+    <span class="n">maxHeapSize</span> <span class="o">=</span> <span 
class="s1">'2g'</span>
+<span class="o">}</span></code></pre>
+</div>
 </div>
+<div class="paragraph">
+<p><code>org.gradle.jvmargs</code> in <code>gradle.properties</code> sizes the 
Gradle daemon, which is a separate process. Raise that only for an 
out-of-memory failure in the build itself, such as during client generation.</p>
+</div>
+<div class="admonitionblock note">
+<table>
+<tr>
+<td class="icon">
+<i class="fa icon-note" title="Note"></i>
+</td>
+<td class="content">
+"Gradle build daemon disappeared unexpectedly" is not the same failure. It is 
usually the operating system&#8217;s out-of-memory killer, meaning 
<code>org.gradle.jvmargs</code> asks for more heap than the machine has; lower 
it.
+</td>
+</tr>
+</table>
 </div>
 </div>
 </div>
@@ -76402,22 +75526,22 @@ mysql <span class="nt">-u</span> root <span 
class="nt">-pmysql</span> fineract_d
 <div class="listingblock">
 <div class="content">
 <pre class="rouge highlight"><code data-lang="bash"><span class="c"># Check 
loan status</span>
-mysql <span class="nt">-u</span> root <span class="nt">-pmysql</span> 
fineract_default <span class="nt">-e</span> <span class="se">\</span>
+psql <span class="nt">-U</span> root <span class="nt">-d</span> 
fineract_default <span class="nt">-c</span> <span class="se">\</span>
   <span class="s2">"SELECT id, account_no, loan_status_id, principal_amount 
</span><span class="se">\</span><span class="s2">
    FROM m_loan ORDER BY id DESC LIMIT 10;"</span>
 
 <span class="c"># Check transactions</span>
-mysql <span class="nt">-u</span> root <span class="nt">-pmysql</span> 
fineract_default <span class="nt">-e</span> <span class="se">\</span>
+psql <span class="nt">-U</span> root <span class="nt">-d</span> 
fineract_default <span class="nt">-c</span> <span class="se">\</span>
   <span class="s2">"SELECT loan_id, transaction_type_enum, amount, 
transaction_date </span><span class="se">\</span><span class="s2">
    FROM m_loan_transaction WHERE loan_id = &lt;loan_id&gt;;"</span>
 
 <span class="c"># Check journal entries</span>
-mysql <span class="nt">-u</span> root <span class="nt">-pmysql</span> 
fineract_default <span class="nt">-e</span> <span class="se">\</span>
+psql <span class="nt">-U</span> root <span class="nt">-d</span> 
fineract_default <span class="nt">-c</span> <span class="se">\</span>
   <span class="s2">"SELECT entry_date, account_id, type_enum, amount 
</span><span class="se">\</span><span class="s2">
    FROM acc_gl_journal_entry WHERE loan_id = &lt;loan_id&gt;;"</span>
 
 <span class="c"># Check configurations</span>
-mysql <span class="nt">-u</span> root <span class="nt">-pmysql</span> 
fineract_default <span class="nt">-e</span> <span class="se">\</span>
+psql <span class="nt">-U</span> root <span class="nt">-d</span> 
fineract_default <span class="nt">-c</span> <span class="se">\</span>
   <span class="s2">"SELECT name, enabled FROM c_configuration </span><span 
class="se">\</span><span class="s2">
    WHERE name LIKE '%business%' OR name LIKE '%enable%';"</span></code></pre>
 </div>
@@ -76426,16 +75550,21 @@ mysql <span class="nt">-u</span> root <span 
class="nt">-pmysql</span> fineract_d
 <div class="sect5">
 <h6 id="_api_response_debugging"><a class="link" 
href="#_api_response_debugging">API Response Debugging</a></h6>
 <div class="paragraph">
-<p>Add logging to test methods:</p>
+<p>A failed Feign call raises <code>CallFailedRuntimeException</code>, which 
carries the status, the developer message and the raw response body:</p>
 </div>
 <div class="listingblock">
 <div class="content">
-<pre class="rouge highlight"><code data-lang="java"><span 
class="nc">Response</span> <span class="n">response</span> <span 
class="o">=</span> <span class="n">loanHelper</span><span 
class="o">.</span><span class="na">applyLoan</span><span class="o">(...);</span>
-<span class="nc">System</span><span class="o">.</span><span 
class="na">out</span><span class="o">.</span><span 
class="na">println</span><span class="o">(</span><span class="s">"Response: 
"</span> <span class="o">+</span> <span class="n">response</span><span 
class="o">.</span><span class="na">asString</span><span class="o">());</span>
-<span class="nc">System</span><span class="o">.</span><span 
class="na">out</span><span class="o">.</span><span 
class="na">println</span><span class="o">(</span><span class="s">"Status Code: 
"</span> <span class="o">+</span> <span class="n">response</span><span 
class="o">.</span><span class="na">getStatusCode</span><span 
class="o">());</span>
-
-<span class="c1">// Or use logger</span>
-<span class="n">log</span><span class="o">.</span><span 
class="na">info</span><span class="o">(</span><span class="s">"Response: 
{}"</span><span class="o">,</span> <span class="n">response</span><span 
class="o">.</span><span class="na">asString</span><span 
class="o">());</span></code></pre>
+<pre class="rouge highlight"><code data-lang="java"><span 
class="nc">CallFailedRuntimeException</span> <span class="n">exception</span> 
<span class="o">=</span> <span class="n">fail</span><span class="o">(()</span> 
<span class="o">-&gt;</span> <span class="n">loanHelper</span><span 
class="o">.</span><span class="na">applyForLoan</span><span 
class="o">(</span><span class="n">request</span><span class="o">));</span>
+<span class="n">log</span><span class="o">.</span><span 
class="na">info</span><span class="o">(</span><span class="s">"status={} 
body={}"</span><span class="o">,</span> <span class="n">exception</span><span 
class="o">.</span><span class="na">getStatus</span><span class="o">(),</span> 
<span class="n">exception</span><span class="o">.</span><span 
class="na">getResponseBody</span><span class="o">());</span></code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>To see the requests and responses themselves, build a client with debug 
logging enabled:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="rouge highlight"><code data-lang="java"><span 
class="nc">FineractFeignClient</span> <span class="n">debugClient</span> <span 
class="o">=</span> <span class="nc">FineractFeignClientHelper</span>
+        <span class="o">.</span><span 
class="na">createNewFineractFeignClient</span><span class="o">(</span><span 
class="n">username</span><span class="o">,</span> <span 
class="n">password</span><span class="o">,</span> <span 
class="kc">true</span><span class="o">);</span></code></pre>
 </div>
 </div>
 </div>
@@ -76463,7 +75592,7 @@ mysql <span class="nt">-u</span> root <span 
class="nt">-pmysql</span> fineract_d
 <div class="ulist">
 <ul>
 <li>
-<p>Extend appropriate base classes (<code>BaseLoanIntegrationTest</code>, 
<code>IntegrationTest</code>)</p>
+<p>Extend <code>FeignLoanTestBase</code> for loan tests, or 
<code>FeignSavingsTestBase</code> for savings tests</p>
 </li>
 <li>
 <p>Use descriptive test method names that explain what is being tested</p>
@@ -76472,7 +75601,7 @@ mysql <span class="nt">-u</span> root <span 
class="nt">-pmysql</span> fineract_d
 <p>Group related tests in the same test class</p>
 </li>
 <li>
-<p>Use <code>@BeforeEach</code> for setup and <code>@AfterEach</code> for 
cleanup</p>
+<p>Rely on the base class for setup; a Feign test needs no 
<code>@BeforeEach</code> block to build a client</p>
 </li>
 <li>
 <p>Follow existing naming conventions</p>
@@ -76607,6 +75736,500 @@ mysql <span class="nt">-u</span> root <span 
class="nt">-pmysql</span> fineract_d
 </div>
 </div>
 </div>
+<div class="sect2">
+<h3 id="_feign_integration_tests"><a class="link" 
href="#_feign_integration_tests">Feign Integration Tests</a></h3>
+<div class="sect3">
+<h4 id="_overview_39"><a class="link" href="#_overview_39">Overview</a></h4>
+<div class="paragraph">
+<p>Apache Fineract is migrating its integration tests from REST Assured to the 
generated Feign client (<code>fineract-client-feign</code>), so that tests 
drive the server through the same strongly typed, OpenAPI-derived models that 
API consumers use. A test written against the generated client stops compiling 
when the API contract changes, rather than failing at runtime — or, worse, 
continuing to pass against a payload the server no longer produces.</p>
+</div>
+<div class="paragraph">
+<p>The shared machinery is the <code>client/feign</code> package tree — the 
<code>FeignLoanTestBase</code> and <code>FeignSavingsTestBase</code> base 
classes and the helpers under <code>client/feign/helpers</code> — together with 
the <code>FeignIntegrationTest</code> root base class one directory above it 
and the <code>FineractFeignClientHelper</code> client factory in 
<code>common</code>. Most migrated tests sit alongside the unmigrated ones in 
the <code>org.apache.fineract.integrationt [...]
+</div>
+<div class="paragraph">
+<p>A Checkstyle rule keeps REST Assured out of that machinery rather than out 
of individual tests, because a leak there is inherited by every test extending 
a Feign base class, while a leak in one test file affects only that test.</p>
+</div>
+<div class="paragraph">
+<p>This chapter covers the guardrail and the conventions that apply to tests 
written against the Feign client. For prerequisites, database setup, and how to 
run a suite, see the <a href="#_integration_testing">Integration Testing</a> 
chapter.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_the_guardrail"><a class="link" href="#_the_guardrail">The 
Guardrail</a></h4>
+<div class="sect4">
+<h5 id="_how_it_works_4"><a class="link" href="#_how_it_works_4">How It 
Works</a></h5>
+<div class="paragraph">
+<p>An <code>IllegalImport</code> check in 
<code>config/checkstyle/checkstyle.xml</code> rejects any 
<code>io.restassured</code> import, and a <code>SuppressionSingleFilter</code> 
narrows it to the shared machinery:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="rouge highlight"><code data-lang="xml"><span 
class="nt">&lt;module</span> <span class="na">name=</span><span 
class="s">"SuppressionSingleFilter"</span><span class="nt">&gt;</span>
+    <span class="nt">&lt;property</span> <span class="na">name=</span><span 
class="s">"id"</span> <span class="na">value=</span><span 
class="s">"FeignNoRestAssured"</span><span class="nt">/&gt;</span>
+    <span class="nt">&lt;property</span> <span class="na">name=</span><span 
class="s">"files"</span>
+              <span class="na">value=</span><span 
class="s">"^(?!.*[\\/]integrationtests[\\/](?:client[\\/]feign[\\/]|client[\\/]FeignIntegrationTest\.java|common[\\/]FineractFeignClientHelper\.java)).*$"</span><span
 class="nt">/&gt;</span>
+<span class="nt">&lt;/module&gt;</span></code></pre>
+</div>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="rouge highlight"><code data-lang="xml"><span 
class="nt">&lt;module</span> <span class="na">name=</span><span 
class="s">"IllegalImport"</span><span class="nt">&gt;</span>
+    <span class="nt">&lt;property</span> <span class="na">name=</span><span 
class="s">"id"</span> <span class="na">value=</span><span 
class="s">"FeignNoRestAssured"</span> <span class="nt">/&gt;</span>
+    <span class="nt">&lt;property</span> <span class="na">name=</span><span 
class="s">"regexp"</span> <span class="na">value=</span><span 
class="s">"true"</span> <span class="nt">/&gt;</span>
+    <span class="nt">&lt;property</span> <span class="na">name=</span><span 
class="s">"illegalPkgs"</span> <span class="na">value=</span><span 
class="s">"^io\.restassured"</span> <span class="nt">/&gt;</span>
+    <span class="nt">&lt;message</span> <span class="na">key=</span><span 
class="s">"import.illegal"</span>
+             <span class="na">value=</span><span class="s">"Feign integration 
tests must call the server through the generated Feign client, not REST 
Assured: ''{0}''"</span> <span class="nt">/&gt;</span>
+<span class="nt">&lt;/module&gt;</span></code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The filter sits at <code>Checker</code> level and the check inside 
<code>TreeWalker</code>. Both carry the same <code>id</code>, so the filter 
applies to this check alone and leaves every other check untouched. The 
<code>files</code> pattern suppresses the check on every path that is not one 
of the three guarded locations, which is what limits its reach.</p>
+</div>
+<div class="paragraph">
+<p>The check runs as part of <code>checkstyleTest</code>, which the 
<code>build-quality-checks.yml</code> workflow executes on every pull request. 
A violation fails the build.</p>
+</div>
+<div class="paragraph">
+<p>Migrated tests outside those locations are not covered. They live in the 
root <code>org.apache.fineract.integrationtests</code> package next to the 
tests that still use REST Assured, and Checkstyle scopes by file path, not by 
supertype, so a rule covering them would fail on their unmigrated neighbours. 
The scope widens as the migration progresses.</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_what_the_check_does_and_does_not_catch"><a class="link" 
href="#_what_the_check_does_and_does_not_catch">What the Check Does and Does 
Not Catch</a></h5>
+<div class="paragraph">
+<p>The <code>Utils.performServerGet</code>, <code>performServerPost</code>, 
<code>performServerPut</code> and <code>performServerDelete</code> helpers in 
<code>org.apache.fineract.integrationtests.common</code> are the other common 
route back to REST Assured. Every one of those overloads takes an 
<code>io.restassured.specification.RequestSpecification</code> as its first 
parameter, so reaching them normally requires the forbidden import.</p>
+</div>
+<div class="paragraph">
+<p>The check reads import statements only, so it is a deterrent rather than a 
proof. Two routes get past it: a fully qualified reference such as 
<code>io.restassured.RestAssured.baseURI</code>, and a <code>var</code> that 
infers a REST Assured type from another method&#8217;s return value without 
ever naming it. Neither appears in the tree today, and neither is something a 
contributor writes by accident, but a reviewer should not treat a green build 
as evidence that no REST Assured call  [...]
+</div>
+<div class="paragraph">
+<p>Utility methods on <code>Utils</code> that do not touch the transport — 
<code>uniqueRandomStringGenerator</code>, <code>getLocalDateOfTenant</code>, 
<code>getDoubleValue</code>, <code>dateFormatter</code> — are unaffected and 
are used widely by migrated tests.</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_running_locally_2"><a class="link" href="#_running_locally_2">Running 
Locally</a></h5>
+<div class="listingblock">
+<div class="content">
+<pre class="rouge highlight"><code data-lang="bash">./gradlew 
:integration-tests:checkstyleTest</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>The report is written to 
<code>integration-tests/build/reports/checkstyle/</code>.</p>
+</div>
+<div class="paragraph">
+<p>The task compiles the test sources first, which for this module means 
generating both clients. Cargo is not involved: 
<code>integration-tests/build.gradle</code> attaches it to the 
<code>test</code> task alone, so a static check never starts a container.</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_when_the_check_fires"><a class="link" 
href="#_when_the_check_fires">When the Check Fires</a></h5>
+<div class="paragraph">
+<p>A violation means a call is being made outside the generated client. 
Resolve it in this order:</p>
+</div>
+<div class="olist arabic">
+<ol class="arabic">
+<li>
+<p><strong>The endpoint is in the generated client.</strong> Use it. This is 
the common case — the generated client covers the public API surface, including 
the <code>/v1/internal</code> endpoints used by tests.</p>
+</li>
+<li>
+<p><strong>The endpoint is in the client, but the generated model is missing a 
field you need.</strong> Fix the schema at source. See <a 
href="#feign-fix-model-at-source">Fixing a Generated Model at Source</a>.</p>
+</li>
+<li>
+<p><strong>The generated client cannot express the call.</strong> Where an 
endpoint declares no response schema the generator types the result as a bare 
<code>String</code> — <code>retrieveOneSavingsAccountTransaction</code> is one 
example — and some multi-step commands are not modelled at all. Give the 
endpoint an <code>@ApiResponse</code> schema where you can (IT-06); otherwise 
call it over plain HTTP and add a Javadoc comment stating why the typed client 
cannot express it.</p>
+</li>
+</ol>
+</div>
+<div class="paragraph">
+<p>Suppressing the rule, dropping the assertion, or substituting a different 
field are not resolutions. See rules IT-05 and IT-09.</p>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_writing_a_feign_integration_test"><a class="link" 
href="#_writing_a_feign_integration_test">Writing a Feign Integration 
Test</a></h4>
+<div class="sect4">
+<h5 id="_test_class_layout"><a class="link" href="#_test_class_layout">Test 
Class Layout</a></h5>
+<div class="paragraph">
+<p>Loan tests extend <code>FeignLoanTestBase</code>, which supplies the 
client, the shared helpers, and the accounting and schedule verification 
methods. Savings tests extend <code>FeignSavingsTestBase</code>.</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="rouge highlight"><code data-lang="java"><span 
class="kn">package</span> <span 
class="nn">org.apache.fineract.integrationtests.client.feign.tests</span><span 
class="o">;</span>
+
+<span class="kn">import</span> <span class="nn">static</span> <span 
class="n">org</span><span class="o">.</span><span class="na">junit</span><span 
class="o">.</span><span class="na">jupiter</span><span class="o">.</span><span 
class="na">api</span><span class="o">.</span><span 
class="na">Assertions</span><span class="o">.</span><span 
class="na">assertEquals</span><span class="o">;</span>
+<span class="kn">import</span> <span class="nn">static</span> <span 
class="n">org</span><span class="o">.</span><span class="na">junit</span><span 
class="o">.</span><span class="na">jupiter</span><span class="o">.</span><span 
class="na">api</span><span class="o">.</span><span 
class="na">Assertions</span><span class="o">.</span><span 
class="na">assertNotNull</span><span class="o">;</span>
+
+<span class="kn">import</span> <span 
class="nn">org.apache.fineract.client.models.GetLoansLoanIdResponse</span><span 
class="o">;</span>
+<span class="kn">import</span> <span 
class="nn">org.apache.fineract.client.models.GetLoansLoanIdStatus</span><span 
class="o">;</span>
+<span class="kn">import</span> <span 
class="nn">org.apache.fineract.client.models.PostLoanProductsRequest</span><span
 class="o">;</span>
+<span class="kn">import</span> <span 
class="nn">org.apache.fineract.integrationtests.client.feign.FeignLoanTestBase</span><span
 class="o">;</span>
+<span class="kn">import</span> <span 
class="nn">org.apache.fineract.integrationtests.common.Utils</span><span 
class="o">;</span>
+<span class="kn">import</span> <span 
class="nn">org.junit.jupiter.api.Test</span><span class="o">;</span>
+
+<span class="kd">public</span> <span class="kd">class</span> <span 
class="nc">FeignLoanCreationTest</span> <span class="kd">extends</span> <span 
class="nc">FeignLoanTestBase</span> <span class="o">{</span>
+
+    <span class="nd">@Test</span>
+    <span class="kt">void</span> <span 
class="nf">testLoanRepayment_FullRepayment</span><span class="o">()</span> 
<span class="o">{</span>
+        <span class="nc">Long</span> <span class="n">clientId</span> <span 
class="o">=</span> <span class="n">createClient</span><span class="o">();</span>
+        <span class="nc">PostLoanProductsRequest</span> <span 
class="n">productRequest</span> <span class="o">=</span> <span 
class="n">onePeriod30DaysNoInterest</span><span class="o">();</span>
+        <span class="nc">Long</span> <span class="n">productId</span> <span 
class="o">=</span> <span class="n">createLoanProduct</span><span 
class="o">(</span><span class="n">productRequest</span><span class="o">);</span>
+        <span class="nc">String</span> <span class="n">todayDate</span> <span 
class="o">=</span> <span class="nc">Utils</span><span class="o">.</span><span 
class="na">dateFormatter</span><span class="o">.</span><span 
class="na">format</span><span class="o">(</span><span 
class="nc">Utils</span><span class="o">.</span><span 
class="na">getLocalDateOfTenant</span><span class="o">());</span>
+        <span class="nc">Long</span> <span class="n">loanId</span> <span 
class="o">=</span> <span class="n">createApproveAndDisburseLoan</span><span 
class="o">(</span><span class="n">clientId</span><span class="o">,</span> <span 
class="n">productId</span><span class="o">,</span> <span 
class="n">todayDate</span><span class="o">,</span> <span 
class="mf">1000.0</span><span class="o">,</span> <span class="mi">1</span><span 
class="o">);</span>
+
+        <span class="nc">Long</span> <span class="n">repaymentId</span> <span 
class="o">=</span> <span class="n">addRepayment</span><span 
class="o">(</span><span class="n">loanId</span><span class="o">,</span> <span 
class="n">repayment</span><span class="o">(</span><span 
class="mf">1000.0</span><span class="o">,</span> <span 
class="n">todayDate</span><span class="o">));</span>
+        <span class="n">assertNotNull</span><span class="o">(</span><span 
class="n">repaymentId</span><span class="o">);</span>
+
+        <span class="nc">GetLoansLoanIdResponse</span> <span 
class="n">loan</span> <span class="o">=</span> <span 
class="n">getLoanDetails</span><span class="o">(</span><span 
class="n">loanId</span><span class="o">);</span>
+        <span class="n">verifyLoanStatus</span><span class="o">(</span><span 
class="n">loan</span><span class="o">,</span> <span 
class="nl">GetLoansLoanIdStatus:</span><span class="o">:</span><span 
class="n">getClosedObligationsMet</span><span class="o">);</span>
+        <span class="n">assertEquals</span><span class="o">(</span><span 
class="mf">0.0</span><span class="o">,</span> <span 
class="nc">Utils</span><span class="o">.</span><span 
class="na">getDoubleValue</span><span class="o">(</span><span 
class="n">loan</span><span class="o">.</span><span 
class="na">getSummary</span><span class="o">().</span><span 
class="na">getTotalOutstanding</span><span class="o">()));</span>
+    <span class="o">}</span>
+<span class="o">}</span></code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p><code>FeignLoanTestBase</code> assigns its helper fields in a 
<code>@BeforeAll</code> named <code>setupHelpers</code>. A subclass that 
declares its own <code>@BeforeAll</code> must give it a different name — a 
static method with the same signature hides the inherited one, and the helpers 
are then never assigned.</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_obtaining_the_client_directly"><a class="link" 
href="#_obtaining_the_client_directly">Obtaining the Client Directly</a></h5>
+<div class="paragraph">
+<p>Inside a test, <code>fineractClient()</code> returns the client the base 
class is using. <code>FineractFeignClientHelper.getFineractFeignClient()</code> 
returns the same shared instance, authenticated with the default backend 
credentials, and is what helpers outside the base class use. Call 
<code>createNewFineractFeignClient(username, password)</code> when a test needs 
a different user.</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_calling_the_api"><a class="link" href="#_calling_the_api">Calling the 
API</a></h5>
+<div class="paragraph">
+<p>Wrap calls in the <code>FeignCalls</code> helpers from 
<code>org.apache.fineract.client.feign.util</code> rather than invoking the API 
interface directly:</p>
+</div>
+<table class="tableblock frame-all grid-all stripes-even stretch">
+<colgroup>
+<col style="width: 25%;">
+<col style="width: 75%;">
+</colgroup>
+<thead>
+<tr>
+<th class="tableblock halign-left valign-top">Method</th>
+<th class="tableblock halign-left valign-top">Use</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>ok(&#8230;&#8203;)</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">A call 
expected to succeed, returning a typed body.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>executeVoid(&#8230;&#8203;)</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">A call 
expected to succeed with no response body.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>fail(&#8230;&#8203;)</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">A call 
expected to fail; returns the <code>CallFailedRuntimeException</code> for 
assertion.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>failVoid(&#8230;&#8203;)</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">As 
<code>fail(&#8230;&#8203;)</code>, for a call with no response body.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock"><code>execute(&#8230;&#8203;)</code></p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">A call 
whose raw <code>FeignException</code> the test handles itself.</p></td>
+</tr>
+</tbody>
+</table>
+<div class="paragraph">
+<p>A direct invocation throws a raw <code>FeignException</code>, which carries 
the HTTP status but not the wrapped error detail the assertion helpers expect. 
<code>ok(&#8230;&#8203;)</code> and <code>fail(&#8230;&#8203;)</code> translate 
it into <code>CallFailedRuntimeException</code>.</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_asserting_failures"><a class="link" 
href="#_asserting_failures">Asserting Failures</a></h5>
+<div class="paragraph">
+<p><code>FeignLoanTestBase.assertErrorGlobalisationCode(exception, 
expectedCode)</code> asserts the specific error code. The top-level 
<code>userMessageGlobalisationCode</code> is often a generic wrapper — 
<code>validation.msg.validation.errors.exist</code> for a validation failure, 
<code>validation.msg.domain.rule.violation</code> for a domain rule — with the 
meaningful code in the first element of the <code>errors</code> array. The 
helper returns the top-level code unless it is one of  [...]
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="rouge highlight"><code data-lang="java"><span 
class="nc">CallFailedRuntimeException</span> <span class="n">exception</span> 
<span class="o">=</span> <span class="n">fail</span><span class="o">(</span>
+        <span class="o">()</span> <span class="o">-&gt;</span> <span 
class="n">fineractClient</span><span class="o">().</span><span 
class="na">loans</span><span class="o">().</span><span 
class="na">handleCommandsLoan</span><span class="o">(</span><span 
class="n">loanId</span><span class="o">,</span> <span 
class="n">request</span><span class="o">,</span> <span 
class="nc">Map</span><span class="o">.</span><span class="na">of</span><span 
class="o">(</span><span class="s">"command"</span><sp [...]
+<span class="n">assertEquals</span><span class="o">(</span><span 
class="mi">403</span><span class="o">,</span> <span 
class="n">exception</span><span class="o">.</span><span 
class="na">getStatus</span><span class="o">());</span>
+<span class="n">assertErrorGlobalisationCode</span><span 
class="o">(</span><span class="n">exception</span><span class="o">,</span> 
<span class="s">"error.msg.loan.approval.cannot.be.a.future.date"</span><span 
class="o">);</span></code></pre>
+</div>
+</div>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_conventions"><a class="link" href="#_conventions">Conventions</a></h4>
+<div class="paragraph">
+<p>The following rules are drawn from review feedback on the migration pull 
requests. They are referenced by identifier in code review.</p>
+</div>
+<div class="sect4">
+<h5 id="_transport"><a class="link" href="#_transport">Transport</a></h5>
+<table class="tableblock frame-all grid-all stripes-even stretch">
+<colgroup>
+<col style="width: 25%;">
+<col style="width: 75%;">
+</colgroup>
+<thead>
+<tr>
+<th class="tableblock halign-left valign-top">Rule</th>
+<th class="tableblock halign-left valign-top">Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">IT-01</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Call the 
server through the generated Feign client. REST Assured is not permitted in the 
<code>&#8230;&#8203;client.feign</code> package tree; the Checkstyle rule above 
enforces this.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">IT-02</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Use the 
<code>FeignCalls</code> helpers rather than invoking the generated API 
interface directly, so failures arrive as 
<code>CallFailedRuntimeException</code>.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">IT-03</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Use the 
Feign client, not the legacy Retrofit client&#8217;s <code>Calls</code> or 
<code>.legacy</code> accessors.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">IT-04</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Plain HTTP 
is acceptable only where the generated client cannot express the call, and only 
with a Javadoc comment recording why.</p></td>
+</tr>
+</tbody>
+</table>
+<div class="admonitionblock caution">
+<table>
+<tr>
+<td class="icon">
+<i class="fa icon-caution" title="Caution"></i>
+</td>
+<td class="content">
+The Retrofit and Feign SDKs both publish models under 
<code>org.apache.fineract.client.models</code>. In 
<code>integration-tests</code> both are on the test classpath, and the one 
declared first in <code>dependencies.gradle</code> wins. When a model change 
appears to have no effect at runtime, check which SDK supplied the class.
+</td>
+</tr>
+</table>
+</div>
+</div>
+<div class="sect4">
+<h5 id="feign-schema-and-sdk"><a class="link" 
href="#feign-schema-and-sdk">Schema and SDK</a></h5>
+<table class="tableblock frame-all grid-all stripes-even stretch">
+<colgroup>
+<col style="width: 25%;">
+<col style="width: 75%;">
+</colgroup>
+<thead>
+<tr>
+<th class="tableblock halign-left valign-top">Rule</th>
+<th class="tableblock halign-left valign-top">Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">IT-05</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">When a 
generated model lacks a field the test needs, add it to the corresponding 
<code>*ApiResourceSwagger</code> DTO and regenerate. Do not work around the gap 
in the test.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">IT-06</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Where an 
endpoint declares no response schema, give it one via <code>@ApiResponse</code> 
rather than introducing a hand-written client interface.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">IT-07</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Schema 
changes must respect the API compatibility rules. See <a 
href="#_api_backward_compatibility">API Backward Compatibility</a>, including 
its guidance on intentional breaking changes.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">IT-08</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">To send an 
explicit JSON <code>null</code>, mark the property <code>nullable</code> in the 
schema; the generator emits <code>JsonNullable&lt;T&gt;</code>. Do not 
hand-roll a request model.</p></td>
+</tr>
+</tbody>
+</table>
+<div class="paragraph">
+<p>After changing a Swagger DTO, regenerate the client and compile every 
module that consumes it, not only the one under test:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="rouge highlight"><code data-lang="bash"><span class="c"># 
Regenerate both clients</span>
+./gradlew :fineract-client:cleanupGeneratedJavaFiles <span class="se">\</span>
+          :fineract-client-feign:cleanupGeneratedJavaFiles
+
+<span class="c"># Then compile the consumers</span>
+./gradlew :fineract-provider:compileTestJava <span class="se">\</span>
+          :integration-tests:compileTestJava <span class="se">\</span>
+          <span class="nt">-PcargoDisabled</span></code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p><code>cleanupGeneratedJavaFiles</code> depends on 
<code>buildJavaSdk</code>, so it regenerates and then copies the result into 
<code>build/generated/java</code>. Running <code>buildJavaSdk</code> on its own 
stops before that copy and leaves the sources the compiler actually uses 
unchanged. Neither task compiles anything downstream, which is why the second 
command is needed — a schema change that breaks a consumer is otherwise found 
only in CI.</p>
+</div>
+<div class="paragraph">
+<p>Regenerate both clients together. <code>integration-tests</code> compiles 
against both, and changing one alone leaves the two disagreeing about the same 
model class.</p>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_assertions"><a class="link" href="#_assertions">Assertions</a></h5>
+<table class="tableblock frame-all grid-all stripes-even stretch">
+<colgroup>
+<col style="width: 25%;">
+<col style="width: 75%;">
+</colgroup>
+<thead>
+<tr>
+<th class="tableblock halign-left valign-top">Rule</th>
+<th class="tableblock halign-left valign-top">Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">IT-09</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Never 
weaken or delete a verification because the generated model does not expose the 
field. Fix the model (IT-05) or keep the check by another route.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">IT-10</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Assert the 
specific status code and error code, not merely that the call threw.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">IT-11</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Verify an 
unordered collection by removing each expected element from a mutable copy of 
the actual collection, then asserting the copy is empty. A size check combined 
with <code>anyMatch</code> passes on duplicates.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">IT-12</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">A 
<code>null</code> expected value means "assert this is null". Do not redefine 
it to mean "skip this check".</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">IT-13</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">A 
migration must not change an expected value. If a migrated test needs a 
different number to pass, establish which value the server actually produces 
before changing it.</p></td>
+</tr>
+</tbody>
+</table>
+<div class="listingblock">
+<div class="title">Verifying an unordered collection</div>
+<div class="content">
+<pre class="rouge highlight"><code data-lang="java"><span 
class="nc">List</span><span class="o">&lt;</span><span 
class="nc">JournalEntry</span><span class="o">&gt;</span> <span 
class="n">remaining</span> <span class="o">=</span> <span class="k">new</span> 
<span class="nc">ArrayList</span><span class="o">&lt;&gt;(</span><span 
class="n">actualEntries</span><span class="o">);</span>
+<span class="k">for</span> <span class="o">(</span><span 
class="nc">JournalEntry</span> <span class="n">expected</span> <span 
class="o">:</span> <span class="n">expectedEntries</span><span 
class="o">)</span> <span class="o">{</span>
+    <span class="nc">Optional</span><span class="o">&lt;</span><span 
class="nc">JournalEntry</span><span class="o">&gt;</span> <span 
class="n">match</span> <span class="o">=</span> <span 
class="n">remaining</span><span class="o">.</span><span 
class="na">stream</span><span class="o">().</span><span 
class="na">filter</span><span class="o">(</span><span class="n">a</span> <span 
class="o">-&gt;</span> <span class="n">matches</span><span 
class="o">(</span><span class="n">a</span><span class=" [...]
+    <span class="n">assertTrue</span><span class="o">(</span><span 
class="n">match</span><span class="o">.</span><span 
class="na">isPresent</span><span class="o">(),</span> <span class="s">"Missing 
entry: "</span> <span class="o">+</span> <span class="n">expected</span><span 
class="o">);</span>
+    <span class="n">remaining</span><span class="o">.</span><span 
class="na">remove</span><span class="o">(</span><span 
class="n">match</span><span class="o">.</span><span class="na">get</span><span 
class="o">());</span>
+<span class="o">}</span>
+<span class="n">assertTrue</span><span class="o">(</span><span 
class="n">remaining</span><span class="o">.</span><span 
class="na">isEmpty</span><span class="o">(),</span> <span class="s">"Unexpected 
entries: "</span> <span class="o">+</span> <span 
class="n">remaining</span><span class="o">);</span></code></pre>
+</div>
+</div>
+</div>
+<div class="sect4">
+<h5 id="_test_structure_2"><a class="link" href="#_test_structure_2">Test 
Structure</a></h5>
+<table class="tableblock frame-all grid-all stripes-even stretch">
+<colgroup>
+<col style="width: 25%;">
+<col style="width: 75%;">
+</colgroup>
+<thead>
+<tr>
+<th class="tableblock halign-left valign-top">Rule</th>
+<th class="tableblock halign-left valign-top">Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">IT-14</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">A helper 
used by one test class belongs in that class. Only behaviour shared across 
classes belongs on the base class or in a helper under 
<code>client/feign/helpers</code>.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">IT-15</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Return the 
response model from a helper, not just the identifier, so callers can assert on 
the rest of the response without a second request.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">IT-16</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Use the 
generated enums and the shared constants in 
<code>client/feign/modules/FeignTestConstants</code>. Do not repeat literal 
codes and identifiers.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">IT-17</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Name 
variables and helper methods for what they hold or do in domain terms, not for 
their type.</p></td>
+</tr>
+</tbody>
+</table>
+</div>
+</div>
+<div class="sect3">
+<h4 id="feign-fix-model-at-source"><a class="link" 
href="#feign-fix-model-at-source">Fixing a Generated Model at Source</a></h4>
+<div class="paragraph">
+<p>When a field the server accepts or returns is absent from the generated 
model, the fix belongs in the Swagger DTO on the server, not in the test.</p>
+</div>
+<div class="olist arabic">
+<ol class="arabic">
+<li>
+<p>Locate the <code>*ApiResourceSwagger</code> class for the resource — for 
example <code>LoanProductsApiResourceSwagger</code> for 
<code>/loanproducts</code>.</p>
+</li>
+<li>
+<p>Add the field to the request or response DTO, matching the name and type 
the server already reads or writes.</p>
+</li>
+<li>
+<p>Regenerate both SDKs and compile every consuming module, using the command 
under <a href="#feign-schema-and-sdk">Schema and SDK</a>.</p>
+</li>
+<li>
+<p>Confirm the API compatibility check still passes, or document the breaking 
change as described in <a href="#_api_backward_compatibility">API Backward 
Compatibility</a>.</p>
+</li>
+<li>
+<p>Replace the workaround in the test with the generated accessor.</p>
+</li>
+</ol>
+</div>
+<div class="paragraph">
+<p>Adding a field to a response schema and widening a request type are both 
additive from the perspective of an existing consumer, but the compatibility 
checker reports some widening changes as violations. <a 
href="#_api_backward_compatibility">API Backward Compatibility</a> describes 
how to handle a violation that is deliberate.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_verifying_a_change"><a class="link" 
href="#_verifying_a_change">Verifying a Change</a></h4>
+<table class="tableblock frame-all grid-all stripes-even stretch">
+<colgroup>
+<col style="width: 25%;">
+<col style="width: 75%;">
+</colgroup>
+<thead>
+<tr>
+<th class="tableblock halign-left valign-top">Rule</th>
+<th class="tableblock halign-left valign-top">Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">IT-18</p></td>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">Compilation and <code>spotlessCheck</code> are not 
verification. Run the tests you touched against a running instance.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">IT-19</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Pass 
<code>-PcargoDisabled</code> when running against an instance you started 
yourself, otherwise the Cargo plugin starts a second Tomcat.</p></td>
+</tr>
+<tr>
+<td class="tableblock halign-left valign-top"><p 
class="tableblock">IT-20</p></td>
+<td class="tableblock halign-left valign-top"><p class="tableblock">Before 
attributing a failure to your change, run the test at the base commit on the 
same instance. Some tests fail independently of the change under 
review.</p></td>
+</tr>
+</tbody>
+</table>
+<div class="paragraph">
+<p>Against a locally running instance:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="rouge highlight"><code data-lang="bash">./gradlew 
:integration-tests:test <span class="nt">-PcargoDisabled</span> <span 
class="se">\</span>
+  <span class="nt">--tests</span> FeignLoanCreationTest</code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Without <code>-PcargoDisabled</code> the Cargo plugin downloads Tomcat, 
deploys <code>fineract-provider.war</code>, and runs the suite against that 
container instead.</p>
+</div>
+</div>
+<div class="sect3">
+<h4 id="_tool_reference_2"><a class="link" href="#_tool_reference_2">Tool 
Reference</a></h4>
+<div class="ulist">
+<ul>
+<li>
+<p><strong>Checkstyle</strong>: <a 
href="https://checkstyle.org/";>checkstyle</a> 13.4.2, configured in 
<code>config/checkstyle/checkstyle.xml</code></p>
+</li>
+<li>
+<p><strong>IllegalImport</strong>: <a 
href="https://checkstyle.org/checks/imports/illegalimport.html";>check 
reference</a></p>
+</li>
+<li>
+<p><strong>SuppressionSingleFilter</strong>: <a 
href="https://checkstyle.org/filters/suppressionsinglefilter.html";>filter 
reference</a></p>
+</li>
+<li>
+<p><strong>Workflow</strong>: 
<code>.github/workflows/build-quality-checks.yml</code></p>
+</li>
+<li>
+<p><strong>Generated client</strong>: <code>fineract-client-feign</code></p>
+</li>
+<li>
+<p><strong>Migrated tests</strong>: 
<code>integration-tests/src/test/java/org/apache/fineract/integrationtests/client/feign</code></p>
+</li>
+</ul>
+</div>
+</div>
+</div>
 </div>
 </div>
 <div class="sect1">
@@ -77592,7 +77215,7 @@ More info at <a 
href="https://docs.asciidoctor.org/asciidoc/latest/stem/stem"; cl
 <div class="content">
 <img 
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnIHN0YW5kYWxvbmU9J25vJz8+PHN2ZyBjb250ZW50U3R5bGVUeXBlPSd0ZXh0L2NzcycgaGVpZ2h0PScxNDdweCcgcHJlc2VydmVBc3BlY3RSYXRpbz0nbm9uZScgc3R5bGU9J3dpZHRoOjQxMHB4O2hlaWdodDoxNDdweDtiYWNrZ3JvdW5kOiNGRkZGRkY7JyB2ZXJzaW9uPScxLjEnIHZpZXdCb3g9JzAgMCA0MTAgMTQ3JyB3aWR0aD0nNDEwcHgnIHhtbG5zOnhsaW5rPSdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHpvb21BbmRQYW49J21hZ25pZnknPjxkZWZzLz48Zz48
 [...]
 </div>
-<div class="title">Figure 14. Release Schedule</div>
+<div class="title">Figure 17. Release Schedule</div>
 </div>
 <div class="admonitionblock tip">
 <table>
@@ -78276,7 +77899,7 @@ Fineract release plugin Gradle tasks are experimental 
and incomplete.
 <div class="content">
 <img 
src="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0nMS4wJyBlbmNvZGluZz0nVVRGLTgnIHN0YW5kYWxvbmU9J25vJz8+PHN2ZyBjb250ZW50U3R5bGVUeXBlPSd0ZXh0L2NzcycgaGVpZ2h0PScxNjI2cHgnIHByZXNlcnZlQXNwZWN0UmF0aW89J25vbmUnIHN0eWxlPSd3aWR0aDozNjlweDtoZWlnaHQ6MTYyNnB4O2JhY2tncm91bmQ6I0ZGRkZGRjsnIHZlcnNpb249JzEuMScgdmlld0JveD0nMCAwIDM2OSAxNjI2JyB3aWR0aD0nMzY5cHgnIHhtbG5zOnhsaW5rPSdodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rJyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHpvb21BbmRQYW49J21hZ25pZnknPjxkZWZzLz48
 [...]
 </div>
-<div class="title">Figure 15. Release Process Diagram</div>
+<div class="title">Figure 18. Release Process Diagram</div>
 </div>
 <div class="sect3">
 <h4 id="_step_1_heads_up_email"><a class="link" 
href="#_step_1_heads_up_email">Step 1: Heads-Up Email</a></h4>
@@ -80466,7 +80089,7 @@ Find more information at <a 
href="https://infra.apache.org/publishing-maven-arti
 <div id="footer">
 <div id="footer-text">
 Version 1.16.0-SNAPSHOT<br>
-Last updated Sat Aug 29 20:04:15 PDT 2026 07:15:38 -0700
+Last updated Mon Aug 31 09:19:07 PDT 2026 07:15:38 -0700
 </div>
 </div>
 </body>

Reply via email to