ADR-038: AI-Assisted AD Documentation Workflow
<!-- MADR 3.0 Template - Markdown Any Decision Records -->
Status
Proposed
Date
2025-12-11
Deciders
- Norbert Bede
Context and Problem Statement
ADR-017 established the foundation for AD_Element description management using pattern-based generation and AI assistance. However, the current approach has limitations:
- Static descriptions only - Generated text lacks runtime business logic context
- No approval workflow - AI-generated content goes directly to migration scripts without review
- Missing contextual help - iDempiere's AD_CtxHelp system is underutilized
- No Java code analysis - Business logic in M*.java classes is not leveraged
We need a comprehensive 3-layer system that:
- Improves basic AD descriptions (Layer 1 - per ADR-017)
- Adds contextual help from Java code analysis (Layer 2)
- Integrates with iDempiere's native approval workflow (Layer 3)
Decision Drivers
- Quality: AI-generated content must be reviewed before application
- Context: Descriptions should reflect actual business logic from Java code
- Native Integration: Use iDempiere's existing AD_CtxHelpSuggestion approval workflow
- Knowledge Reuse: Store extracted knowledge in vector DB for RAG
- Scalability: Handle 4000+ elements, 500+ tabs, 200+ processes systematically
- Contribution Path: Enable submission of improvements to iDempiere core
Considered Options
- Direct migration scripts only - Generate SQL updates without approval workflow
- External review system - Build custom approval UI outside iDempiere
- Native iDempiere integration - Use AD_CtxHelpSuggestion with AI generation
Decision Outcome
Chosen option: "Native iDempiere integration", because it leverages existing iDempiere infrastructure, provides familiar UI for reviewers, and enables both tenant-specific and core contribution workflows.
Confirmation
- [ ]
dict analyzecommand identifies missing descriptions with quality metrics - [ ]
dict describegenerates content following ADR-001 standards - [ ]
rag ingest --source java_codeextracts business logic to vector DB - [ ]
dict suggestcreates AD_CtxHelpSuggestion records for review - [ ] Approved suggestions can be exported as migration scripts
Pros and Cons of the Options
Option 1: Direct migration scripts only
Continue with ADR-017 approach - generate SQL directly.
- Good, because simple implementation
- Good, because immediate application
- Bad, because no human review before changes
- Bad, because errors propagate to production
- Bad, because no contribution workflow to core
Option 2: External review system
Build custom web UI for reviewing AI suggestions.
- Good, because full control over workflow
- Good, because could add advanced features
- Bad, because duplicate effort (iDempiere already has this)
- Bad, because separate system to maintain
- Bad, because users must learn new UI
Option 3: Native iDempiere integration (Chosen)
Use AD_CtxHelpSuggestion table and Context Help Suggestion window.
- Good, because uses existing iDempiere infrastructure
- Good, because familiar UI for administrators
- Good, because supports tenant customization vs core contribution
- Good, because built-in Compare/Accept/Reject workflow
- Good, because audit trail through standard iDempiere logging
- Neutral, because requires iDempiere instance for review
- Bad, because limited to AD_CtxHelp structure (not direct AD_Element updates)
Architecture
Three-Layer System
================================================================================
LAYER 1: BASIC AD DESCRIPTIONS (Static, Migration Scripts)
Extends ADR-017
================================================================================
Input Sources:
+-----------------+ +-----------------+ +-----------------+
| Pattern Rules | | Standard Cols | | idempiere- |
| (*_UU, Is*, etc)| | Dictionary | | description- |
| | | | | writer Agent |
+-----------------+ +-----------------+ +-----------------+
| | |
+-----------------------+-----------------------+
|
v
+-----------------------------------------------------------------------+
| ElementDescriptionService |
| - PatternGenerator: Rule-based descriptions |
| - StandardColumnMapper: Fixed descriptions for common columns |
| - AIDescriptionGenerator: Claude agent for complex cases |
+-----------------------------------------------------------------------+
|
v
+-----------------------------------------------------------------------+
| Output: Migration Scripts |
| - PostgreSQL: UPDATE AD_Element SET Description='...' WHERE ... |
| - Oracle: UPDATE AD_Element SET Description='...' WHERE ... |
| - 2Pack XML: For deployment via iDempiere import |
+-----------------------------------------------------------------------+
Target Tables:
+------------------+----------------------------------------+
| AD_Element | Name, Description, Help, PrintName |
| AD_Window | Name, Description, Help |
| AD_Tab | Name, Description, Help, CommitWarning |
| AD_Field | Name, Description, Help |
| AD_Process | Name, Description, Help |
| AD_Process_Para | Name, Description, Help |
| AD_Menu | Name, Description |
| AD_Message | MsgText, MsgTip |
| AD_Ref_List | Name, Description |
+------------------+----------------------------------------+
================================================================================
LAYER 2: CONTEXTUAL HELP (Java Code Analysis -> Vector DB)
NEW in this ADR
================================================================================
Java Source Analysis:
+-----------------+ +-----------------+ +-----------------+
| M*.java | | Process Classes | | Callout Classes |
| Model Classes | | (SvrProcess) | | (CalloutEngine) |
+-----------------+ +-----------------+ +-----------------+
| | |
v v v
+-----------------------------------------------------------------------+
| JavaCodeAnalyzer |
| - Parse beforeSave(), afterSave() methods |
| - Extract validation rules (FillMandatory, constraints) |
| - Identify calculations (LineNetAmt = Qty * Price) |
| - Find field dependencies (triggers, callouts) |
| - Detect read-only conditions |
+-----------------------------------------------------------------------+
|
v
+-----------------------------------------------------------------------+
| Vector DB Storage (pgvector) |
+-----------------------------------------------------------------------+
| source_type | class_name | method | column | content |
+-------------+-------------+------------+-------------+----------------+
| java_model | MOrderLine | beforeSave | QtyOrdered | Validation: |
| | | | | must be > 0. |
| | | | | Triggers: ... |
+-------------+-------------+------------+-------------+----------------+
| java_model | MOrderLine | afterSave | QtyReserved | Calculated: |
| | | | | Updates M_ |
| | | | | Storage... |
+-------------+-------------+------------+-------------+----------------+
| java_process| GenerateInv | doIt | - | Creates C_ |
| | | | | Invoice from |
| | | | | C_Order... |
+-----------------------------------------------------------------------+
Knowledge Types Extracted:
+------------------------+----------------------------------------------+
| Validation Rules | "QtyOrdered must be positive" |
| Calculations | "LineNetAmt = Qty * Price - Discount" |
| Triggers | "Changing Qty triggers price recalculation" |
| Read-only Conditions | "Cannot modify after document is processed" |
| Field Dependencies | "Product selection updates UOM and Price" |
| Business Logic | "Reserved quantity reduces available stock" |
+------------------------+----------------------------------------------+
================================================================================
LAYER 3: CONTEXT HELP SUGGESTION + AI APPROVAL WORKFLOW
NEW in this ADR - Uses native iDempiere tables
================================================================================
iDempiere Native Tables:
+------------------------+----------------------------------------------+
| AD_CtxHelp | Context help definitions |
| | - CtxType: tab, process, form, workflow |
| | - Links to specific AD entity |
+------------------------+----------------------------------------------+
| AD_CtxHelpMsg | Help message content (2000 chars) |
| | - Actual help text displayed in UI |
+------------------------+----------------------------------------------+
| AD_CtxHelpMsg_Trl | Translations for multi-language |
+------------------------+----------------------------------------------+
| AD_CtxHelpSuggestion | Approval workflow table |
| | - MsgText: Suggested help text |
| | - IsApproved: Approval status |
| | - AcceptSuggestion: Button to accept |
| | - RejectSuggestion: Button to reject |
| | - CompareSuggestion: Button to compare |
| | - IsSaveAsTenantCustomization: Scope flag |
+------------------------+----------------------------------------------+
AI Approval Workflow:
+-----------------------------------------------------------------------+
| |
| 1. AI GENERATES |
| +-------------+ |
| | CLI: dict | --> Analyze AD + Java code + Vector DB context |
| | suggest | --> Generate improved help text |
| +------+------+ |
| | |
| v |
| 2. CREATE SUGGESTION |
| +---------------------------------------------------------------+ |
| | INSERT INTO AD_CtxHelpSuggestion | |
| | (AD_CtxHelp_ID, AD_CtxHelpMsg_ID, MsgText, | |
| | AD_User_ID, AD_Language, IsApproved='N') | |
| +------+--------------------------------------------------------+ |
| | |
| v |
| 3. HUMAN REVIEW (iDempiere UI) |
| +---------------------------------------------------------------+ |
| | Window: Context Help Suggestion (ID-200088) | |
| | | |
| | +-----------------------------------------------------------+ | |
| | | Current Help: | Suggested Help: | | |
| | | "The quantity..." | "The Quantity Ordered field defines | | |
| | | | the number of units requested. This | | |
| | | | value must be positive and triggers | | |
| | | | inventory reservation when saved..." | | |
| | +-----------------------------------------------------------+ | |
| | | |
| | Actions: [Compare] [Reject] [Accept] | |
| | | |
| | [ ] Save as Tenant Customization (local only) | |
| | [x] Contribute to Core (for migration script) | |
| +------+--------------------------------------------------------+ |
| | |
| v |
| 4. APPLY APPROVED |
| +---------------------------------------------------------------+ |
| | If Tenant Customization: | |
| | UPDATE AD_CtxHelpMsg SET MsgText = suggestion.MsgText | |
| | WHERE AD_Client_ID = suggestion.AD_Client_ID | |
| | | |
| | If Core Contribution: | |
| | Export approved suggestions as migration script | |
| | Submit to iDempiere JIRA for review | |
| +---------------------------------------------------------------+ |
| |
+-----------------------------------------------------------------------+
CLI Commands
Layer 1 Commands (extends ADR-017)
# Analyze missing descriptions
idempiere-cli dict analyze --missing
idempiere-cli dict analyze --poor-quality --min-length 10
# Generate descriptions using patterns + AI
idempiere-cli dict describe --element C_Order_ID
idempiere-cli dict describe --pattern "Qty*" --ai
idempiere-cli dict describe --table AD_Element --missing
# Generate migration scripts
idempiere-cli dict migrate --output ./migration --format sql
idempiere-cli dict migrate --output ./migration --format 2pack
Layer 2 Commands (Java Analysis)
# Ingest Java source code into vector DB
idempiere-cli rag ingest --source java_code --path /path/to/idempiere
# Search for business logic context
idempiere-cli rag search "QtyReserved validation rules"
idempiere-cli rag search "order line calculations"
# Analyze specific model class
idempiere-cli dict analyze-java --class MOrderLine
idempiere-cli dict analyze-java --path /path/to/idempiere --all
Layer 3 Commands (Approval Workflow)
# Create suggestions for review in iDempiere
idempiere-cli dict suggest --window "Sales Order"
idempiere-cli dict suggest --tab C_OrderLine
idempiere-cli dict suggest --process "Generate Invoice"
idempiere-cli dict suggest --all-missing --batch-size 100
# Export approved suggestions as migration scripts
idempiere-cli dict export-approved --output ./migration
idempiere-cli dict export-approved --since 2025-01-01 --format sql
# Status report
idempiere-cli dict status
# Output:
# Suggestions: 150 pending, 45 approved, 10 rejected
# Coverage: AD_Element 78%, AD_Tab 45%, AD_Process 62%
Vector DB Schema for Java Analysis
-- Extension to existing RAG embeddings table
-- Adds java_code source type with rich metadata
-- Metadata structure for java_code source:
{
"source_type": "java_code",
"class_name": "MOrderLine",
"class_type": "model", -- model, process, callout, validator
"method_name": "beforeSave",
"column_name": "QtyOrdered", -- null for process-level
"table_name": "C_OrderLine",
"package": "org.compiere.model",
"file_path": "org.adempiere.base/src/org/compiere/model/MOrderLine.java",
"line_start": 245,
"line_end": 280,
"logic_type": "validation", -- validation, calculation, trigger, readonly
"content": "Validation: QtyOrdered must be positive. Throws exception if <= 0."
}
Glossary Integration
ERP terminology glossaries (e.g., Slovak ERP Glossary from iDEmpiereCLDE) can be ingested into vector DB:
# Ingest glossary for translation consistency
idempiere-cli rag ingest --source glossary --file SLOVAK_ERP_GLOSSARY.md --language sk_SK
# Use glossary in translation generation
idempiere-cli dict describe --element C_Order_ID --language sk_SK
# AI retrieves: "Sales Order = Objednavka predaja" from glossary
# Generates consistent Slovak terminology
Glossary entry structure in vector DB:
{
"source_type": "glossary",
"language": "sk_SK",
"domain": "sales",
"english_term": "Sales Order",
"translated_term": "Objednavka predaja",
"alternative": "Predajna objednavka",
"context": "Primary term for customer purchase orders"
}
LangChain4j Integration
The workflow leverages the existing LangChain4j infrastructure (ADR-013, ADR-023) with a dedicated AI Service for documentation generation.
DictAssistant AI Service
/**
* LangChain4j AI Service for AD documentation generation.
*
* Integrates with existing tools (RAG, Registry) and adds
* specialized description generation capabilities.
*/
@RegisterAiService(
tools = {
DictTools.class, // NEW: Description generation tools
RagTools.class, // Existing: Vector DB search
RegistryTools.class // Existing: AD metadata access
}
)
public interface DictAssistant {
@SystemMessage("""
You are an iDempiere Application Dictionary documentation expert.
Your role is to generate high-quality Name, Description, and Help text
for AD elements following iDempiere documentation standards (ADR-001).
Key rules:
- ALWAYS use "iDempiere" as product name
- NEVER expose class names or technical implementation details
- Convert column names to human-readable form (C_BPartner_ID -> Business Partner)
- Description: 1 sentence, max 255 chars
- Help: 1-3 sentences, max 2000 chars, complete sentences
When generating descriptions:
1. First search the knowledge base for context (Java code, existing examples)
2. Apply pattern rules for standard columns (*_UU, Is*, Qty*, etc.)
3. Generate text following the documentation standards
4. Ensure Description and Help use different vocabulary
""")
@UserMessage("""
Generate documentation for AD element: {elementName}
Context:
- Column name: {columnName}
- Current Description: {currentDescription}
- Current Help: {currentHelp}
- Used in tables: {tables}
- Used in windows: {windows}
Requirements:
- Pattern type: {patternType}
- Language: {language}
Search the knowledge base for business logic context, then generate
appropriate Name, Description, and Help text.
""")
DocumentationResult generateDocumentation(
String elementName,
String columnName,
String currentDescription,
String currentHelp,
String tables,
String windows,
String patternType,
String language
);
@UserMessage("""
Analyze the quality of this AD element documentation:
Element: {elementName}
Description: {description}
Help: {help}
Evaluate against iDempiere standards and return:
- Quality score (0-100)
- Issues found
- Improvement suggestions
""")
QualityAssessment assessQuality(
String elementName,
String description,
String help
);
@UserMessage("""
Search for similar AD elements to use as examples for: {elementName}
Pattern: {pattern}
Domain: {domain}
Return 3-5 well-documented elements with similar patterns.
""")
String findExamples(String elementName, String pattern, String domain);
}
DictTools - Tool Annotations
/**
* LangChain4j tools for dictionary documentation operations.
*/
@ApplicationScoped
public class DictTools {
@Inject
ElementDescriptionService descriptionService;
@Inject
CtxHelpSuggestionService suggestionService;
@Tool("Generate description for an AD element using pattern rules")
public String generatePatternDescription(
@P("Column name") String columnName,
@P("Pattern type: UU, ID, Is, Qty, Amt, Date, Acct, Standard") String pattern
) {
return descriptionService.generateByPattern(columnName, pattern);
}
@Tool("Create a context help suggestion for review in iDempiere")
public String createSuggestion(
@P("AD_CtxHelp_ID") int ctxHelpId,
@P("Suggested help text") String msgText,
@P("Language code") String language
) {
return suggestionService.createSuggestion(ctxHelpId, msgText, language);
}
@Tool("Get Java business logic context for a column from vector DB")
public String getJavaContext(
@P("Column name") String columnName,
@P("Table name") String tableName
) {
return ragService.searchJavaCode(columnName, tableName);
}
@Tool("List AD elements with missing or poor quality descriptions")
public String analyzeElements(
@P("Pattern filter: %, *_UU, Is*, etc.") String pattern,
@P("Minimum quality score 0-100") int minScore
) {
return descriptionService.analyzeQuality(pattern, minScore);
}
}
Workflow Integration
┌─────────────────────────────────────────────────────────────────────────┐
│ LangChain4j Workflow │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ User: "Generate help for QtyOrdered field" │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ DictAssistant.generateDocumentation() │ │
│ │ │ │
│ │ 1. @Tool getJavaContext("QtyOrdered", "C_OrderLine") │ │
│ │ → RAG search: "MOrderLine beforeSave QtyOrdered validation" │ │
│ │ → Returns: "Must be > 0, triggers reservation update" │ │
│ │ │ │
│ │ 2. @Tool generatePatternDescription("QtyOrdered", "Qty") │ │
│ │ → Pattern: "Quantity of {subject}" │ │
│ │ → Returns: "Quantity ordered in document UOM" │ │
│ │ │ │
│ │ 3. LLM combines context + pattern + standards │ │
│ │ → Generates: Description + Help following ADR-001 │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ DictAssistant.assessQuality() │ │
│ │ - Score: 85/100 │ │
│ │ - Issues: None │ │
│ │ - Ready for suggestion creation │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ @Tool createSuggestion() │ │
│ │ - INSERT INTO AD_CtxHelpSuggestion │ │
│ │ - Returns: "Suggestion created, pending review" │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Integration with Existing Tools
The DictTools integrate with existing LangChain4j tools:
| Existing Tool | Usage in Dict Workflow |
|---|---|
RagTools.searchKnowledge |
Find Java code context, glossary terms |
RegistryTools.describeTable |
Get AD_Element metadata, column info |
RegistryTools.listColumns |
Find related columns for context |
QueryTools.executeQuery |
Query AD_CtxHelp, AD_CtxHelpSuggestion |
CLI Command Integration
@Command(name = "describe", description = "Generate descriptions using AI")
public class DictDescribeCommand implements Callable<Integer> {
@Inject
DictAssistant dictAssistant; // LangChain4j AI Service
@Option(names = "--element")
String elementName;
@Option(names = "--ai", description = "Use AI for generation")
boolean useAi;
@Override
public Integer call() {
if (useAi) {
// Use LangChain4j workflow
var result = dictAssistant.generateDocumentation(
elementName,
columnName,
currentDesc,
currentHelp,
tables,
windows,
detectPattern(columnName),
"en_US"
);
// Output result...
} else {
// Use pattern-only generation
var result = patternService.generate(elementName);
// Output result...
}
return 0;
}
}
Implementation Plan
Phase 1: Layer 2 - Java Code Analysis
-
Create
JavaCodeAnalyzerservice- AST parsing using JavaParser library
- Extract beforeSave/afterSave logic from M*.java
- Parse SvrProcess.doIt() for process descriptions
- Identify callout dependencies
-
Create
JavaCodeIngestorfor RAG- Implement
KnowledgeIngestorinterface - Chunk code by method/logic block
- Generate embeddings with rich metadata
- Add
rag ingest --source java_codecommand
- Implement
-
Enhance description generation
- Query vector DB for column context
- Combine pattern rules with Java logic
- Generate richer Help text
Phase 2: Layer 3 - Approval Workflow
-
Create
CtxHelpSuggestionService- Map AD_Element/Window/Tab to AD_CtxHelp
- Insert suggestions into AD_CtxHelpSuggestion
- Handle batch creation with progress
-
Add CLI commands
dict suggest- Create suggestionsdict export-approved- Export for migrationdict status- Show workflow statistics
-
Documentation
- User guide for review workflow
- Contribution guide for core submissions
Phase 3: Glossary and Translation
-
Create
GlossaryIngestor- Parse markdown glossary files
- Support multiple languages
- Domain categorization (sales, purchase, warehouse, etc.)
-
Translation-aware generation
- Query glossary for terminology
- Ensure consistency across translations
- Support AD_Element_Trl updates
More Information
iDempiere Context Help Tables
| Table | Purpose | Key Columns |
|---|---|---|
| AD_CtxHelp | Context help definitions | CtxType, Name, Description |
| AD_CtxHelpMsg | Help message content | MsgText (2000 chars) |
| AD_CtxHelpMsg_Trl | Translations | AD_Language, MsgText, IsTranslated |
| AD_CtxHelpSuggestion | Approval workflow | MsgText, IsApproved, AcceptSuggestion, RejectSuggestion |
CtxType Values
| Value | Description |
|---|---|
| T | Tab |
| P | Process |
| F | Form |
| W | Workflow |
| I | Info Window |
Related ADRs
- ADR-017 - AD_Element Description Management (Layer 1 foundation)
- ADR-021 - RAG Architecture (Vector DB infrastructure)
- ADR-011 - AI Integration (Claude agent setup)
References
- Context Help Window (ID-200034)
- Context Help Suggestion (ID-200088)
- iDempiere Application Dictionary
- Translation Export/Import
Appendix A: AD_CtxHelpSuggestion Table Structure
CREATE TABLE ad_ctxhelpsuggestion (
ad_ctxhelpsuggestion_id NUMERIC(10,0) NOT NULL,
ad_client_id NUMERIC(10,0) NOT NULL,
ad_org_id NUMERIC(10,0) NOT NULL,
ad_ctxhelp_id NUMERIC(10,0) NOT NULL,
ad_ctxhelpmsg_id NUMERIC(10,0) NOT NULL,
ad_ctxhelpsuggestion_uu VARCHAR(36) NOT NULL,
ad_language VARCHAR(6) NOT NULL,
ad_userclient_id NUMERIC(10,0) NOT NULL,
ad_user_id NUMERIC(10,0) NOT NULL,
msgtext VARCHAR(2000) NOT NULL,
isactive CHAR(1) NOT NULL DEFAULT 'Y',
isapproved CHAR(1) NOT NULL DEFAULT 'N',
processed CHAR(1) NOT NULL DEFAULT 'N',
created TIMESTAMP NOT NULL,
createdby NUMERIC(10,0) NOT NULL,
updated TIMESTAMP NOT NULL,
updatedby NUMERIC(10,0) NOT NULL,
-- Workflow buttons
acceptsuggestion CHAR(1),
rejectsuggestion CHAR(1),
comparesuggestion CHAR(1),
-- Scope control
issaveastenantcustomization CHAR(1) NOT NULL DEFAULT 'N',
CONSTRAINT ad_ctxhelpsuggestion_key PRIMARY KEY (ad_ctxhelpsuggestion_id)
);
Appendix B: Sample Workflow
Example: Improving QtyOrdered Help Text
Step 1: Analyze current state
$ idempiere-cli dict analyze --element QtyOrdered
Element: QtyOrdered (AD_Element_ID: 531)
Current Description: "Ordered Quantity"
Current Help: NULL (missing)
Used in: C_OrderLine, C_InvoiceLine, M_InOutLine (12 windows)
Quality Score: 0.3 (needs improvement)
Step 2: Generate with Java context
$ idempiere-cli dict describe --element QtyOrdered --ai --with-java-context
Analyzing Java code for QtyOrdered...
Found in MOrderLine.beforeSave(): validation (must be > 0)
Found in MOrderLine.afterSave(): triggers reservation update
Found in MInOutLine: compared with QtyEntered
Generated Description: "Quantity ordered in the document's unit of measure"
Generated Help: "The Quantity Ordered field specifies the number of units
requested in this document line. This value must be positive and cannot be
zero. When saved, it triggers automatic inventory reservation updates.
The ordered quantity may differ from the delivered quantity tracked in
shipment documents. For unit of measure conversions, compare with the
Quantity Entered field."
Step 3: Create suggestion for review
$ idempiere-cli dict suggest --element QtyOrdered
Created suggestion:
AD_CtxHelpSuggestion_ID: 1000045
Status: Pending review
Review in iDempiere: Window > Context Help Suggestion
Step 4: Review in iDempiere UI
- Administrator opens Context Help Suggestion window
- Clicks [Compare] to see current vs suggested
- Clicks [Accept] to approve
- Optionally checks "Save as Tenant Customization"
Step 5: Export approved for core contribution
$ idempiere-cli dict export-approved --since 2025-12-01 --output ./migration
Exported 45 approved suggestions to:
./migration/postgresql/202512111200_AD-Element-Help.sql
./migration/oracle/202512111200_AD-Element-Help.sql
Ready for JIRA submission: IDEMPIERE-XXXX