iDempiereで画面(フォーム)を追加してみる

前回はLDAP認証について調査しました。
iDempiereの機能を拡張していく際に画面/UIも必要になります。
新しい画面を追加して機能を組み込む場合、最初に行うのはフォームの追加です。
今回は、新しい画面を追加（非常にシンプルな画面です）する手順を紹介したいと思います。

## 参考情報
こちらを参照にさせていただきました。

https://wiki.idempiere.org/en/Developing_Plug-Ins_-_IFormFactory_(ZK_Webui)

## 検証環境
前回の記事で投稿した内容をご確認ください。
* [iDempiereのDocker上での環境構築](https://qiita.com/idempiere-fan/items/ca8520f567c5939cc5cc)
* [iDempiereの開発環境構築（Windows環境）](https://qiita.com/idempiere-fan/items/2d8fd80d2f80f2478e4f)

今回も、こちらの環境を使用します。

## 今回の実装概要
今回は、テキストボックスを１つ配置し、任意の文字列を入力後にエンターキーが押されたときのイベントを処理する画面を実装します。
この実装したFormをメニューから呼び出すように、iDempiere上からMenuおよびForm画面を使用して設定を行います。非常にシンプルな画面です。
なお、今回の実装および動作結果を先に案内します。

* Javaコード記述結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/f3452f38-19d0-b664-970c-97a03caf1b69.png)

* フォーム動作結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/2295e763-3498-ca47-936b-c80cc41c665f.png)

それでは、具体的な実装手順を紹介します。
## ソースコードの記述
事前に、上記検証環境にある内容を参照しiDempiereサービスおよびPostgreSQLサーバを起動しておきます。
それではFormを実装していきます。
Ecliseを起動し以下の手順を進めます。

### プラグインプロジェクトの準備

#### 1\. プラグインプロジェクト、各パッケージ
プラグインプロジェクト、各パッケージを用意します。
なお、プラグインプロジェクトの[準備手順の詳細はこちらにも記載しています](https://qiita.com/idempiere-fan/items/519507eb6fa9a94b791e#process%E6%A9%9F%E8%83%BD%E3%81%AE%E5%AE%9F%E8%A3%85)ので参照にどうぞ。

| # | 区分 | 内容 |
|:-:|:-|:--|
| 1  | プロジェクト名  | org.idempierefan.simpleform  |
| 2 |  パッケージ名(Form) | org.idempierefan.simpleform.app  |
| 3 |  パッケージ名(Factory) | org.idempierefan.simpleform.factory  |

#### 2\. クラス
クラスを用意します。
| # | パッケージ | クラス |
|:-:|:-|:--|
| 1 |  org.idempierefan.simpleform.app  | SimpleForm |
| 2 |  org.idempierefan.simpleform.factory  | SimpleFormFactory |

#### 3\. コンポーネント定義(Component Definition)
コンポーネント定義(Component Definition)を用意します。
| # | 格納場所 | ファイル名 |
|:-:|:-|:--|
| 1 |  OSGI-INF  | SimpleFormFactory.xml |

#### 4\. 追加結果
上記内容を追加した結果、以下の状態になっていると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/3da93112-b426-f18e-e087-1e8010f5bbfe.png)

つづいて、追加したプラグインの設定をします。
#### 5\. MANIFEST.MFの編集
ProjectExplorerから以下のファイルを開きます。
| # | 格納場所 | ファイル名 |
|:-:|:-|:--|
| 1 |  META-INF  | MANIFEST.MF |

[MANIFEST.MF]タブをクリックし、以下の内容を記載します。
```javascript
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Simpleform
Bundle-SymbolicName: org.idempierefan.simpleform;singleton:=true
Bundle-Version: 1.0.0.qualifier
Automatic-Module-Name: org.idempierefan.simpleform
Bundle-RequiredExecutionEnvironment: JavaSE-11
Service-Component: OSGI-INF/SimpleFormFactory.xml
Bundle-ActivationPolicy: lazy
Import-Package: org.adempiere.webui.factory,
 org.adempiere.webui.panel,
 org.zkoss.zk.ui;version="9.6.3",
 org.zkoss.zk.ui.event;version="9.6.3",
 org.zkoss.zul,
 org.zkoss.zul.impl;version="9.6.3"
Require-Bundle: org.adempiere.base;bundle-version="10.0.0",
 zcommon;bundle-version="8.0.1"
```
#### 6\. SimpleFormFactory.xmlの編集
つづいて、SimpleFormFactory.xmlを同様に編集します。
| # | 格納場所 | ファイル名 |
|:-:|:-|:--|
| 1 | OSGI-INF  | SimpleFormFactory.xml |
```xml
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.idempierefan.simpleform">
   <implementation class="org.idempierefan.simpleform.factory.SimpleFormFactory"/>
   <property name="service.ranking" type="Integer" value="5"/>
   <service>
      <provide interface="org.adempiere.webui.factory.IFormFactory"/>
   </service>
</scr:component>
```
#### 7\. Run configurationの設定変更
Eclipseメニューから[Run]-[Run configurations]をクリックします。
```org.idempierefan.simpleform```のチェックを```ON```にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/b8f82599-cb5a-7f4f-26a3-cc129fa2e710.png)

以上で設定は完了です。
続いて、Javaコードを記述していきます。

### Javaコードの記述
以下のクラスを実装します。
| # | パッケージ | クラス |
|:-:|:-|:--|
| 1 |  org.idempierefan.simpleform.app  | SimpleForm |
| 2 |  org.idempierefan.simpleform.factory  | SimpleFormFactory |

#### 1\. SimpleFormクラスの記述内容
```java
package org.idempierefan.simpleform.app;

import org.adempiere.webui.panel.ADForm;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.Textbox;

public class SimpleForm extends ADForm {
	
	Textbox intxt = new Textbox();
	
	@Override
	protected void initForm() {
		// Add an event listener for the ENTER key on the text box
		intxt.addEventListener(Events.ON_OK, this);
		// Append the text box to the form
		this.appendChild(intxt);
		// Print a message to the console
		System.out.println("Enter Textbox: ");
	}
	
	@Override
	public void onEvent(Event event) throws WrongValueException {
	    // Check if the event is triggered by the ENTER key on the text box
	    if (event.getTarget().equals(intxt) && 
	    		event.getName().equals(Events.ON_OK)) {
	    	try {
				// Print the content of the text box to the console
				System.out.println("Textbox: " + intxt.getText());
			} catch (WrongValueException e) {
				// Handle the exception if there is a wrong value
				e.printStackTrace();
			}
	    }
	}
}
```

#### 2\. SimpleFormFactoryクラスの記述内容
```java
package org.idempierefan.simpleform.factory;

import org.adempiere.webui.factory.IFormFactory;
import org.adempiere.webui.panel.ADForm;
import org.adempiere.webui.panel.IFormController;

public class SimpleFormFactory implements IFormFactory {

	@Override
	public ADForm newFormInstance(String formName) {

		Object form = null;
		
		// Check if the form name starts with the specified package
	     if (formName.startsWith("org.idempierefan.simpleform.app")) {
	    	 // Get the class loader and attempt to load the specified class
	           ClassLoader cl = getClass().getClassLoader();
	           Class<?> clazz = null;

			  try {
				clazz = cl.loadClass(formName);
		      } catch (Exception e) {
		            // Return null if there's an exception loading the class
		            return null;
			  }
			  
		         try {
		        	 // Create an instance of the loaded class
				    form = clazz.getDeclaredConstructor().newInstance();
				  } catch (Exception e) {
					  // Return null if there's an exception creating the instance
					  return null;
			      }

		      if (form != null) {
		    	 // Check if the instance is an ADForm
			     if (form instanceof ADForm) {
			    	 return (ADForm) form;
			     }
			     // If it's an IFormController, extract the ADForm and set the custom form controller
			     else if (form instanceof IFormController) {
					IFormController controller = (IFormController) form;
					ADForm adForm = controller.getForm();
					adForm.setICustomForm(controller);
					return adForm;
			     }
		     }
	     }
	     
	     // Return null if the form name does not match the specified package
	     return null;
	}
}
```

以上でJavaコードの記述は完了です。
つづいて、iDempiereの画面から設定を行います。

## iDempiere設定
iDempiereにログインし、設定を行います。
iDempiere画面をブラウザ「https://localhost:8443/webui を入力」 から開き、以下のユーザおよびロールを指定しログインします。。
| # | 画面 | 項目 | 指定内容 |
|:-:|:-:|:-|:-|
| 1  | ログイン  |  EMail | superuser@oss-erp.co.jp  |
| 2  |   | Password  |  System |
| 3  |   | Language  |  English |
| 4  | テナント/ロール  | Tenant  |  System |
| 5  |  | Role  |  System Administrator |

### Formの登録
Form新規画面から登録します。
#### Form新規登録
検索欄に```Form```を入力し、リスト表示されたForm行から```新規作成```アイコンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/efcdd55c-a02d-a83e-127d-f89d6266fec7.png)

#### 名称、クラスの入力
```Name```に記載した内容を後続の設定で使用します。
```Classname```には上記で実装したFormのクラス名(```org.idempierefan.simpleform.app.SimpleForm```)を記載します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/5353e02c-9577-8ad8-f686-c8ee4a21417a.png)

以上でFormの登録は完了です。
つづいて、Menuを追加します。

### Menuの登録
iDempiereトップ画面を開きます。
Menu新規画面から登録します。
#### Menu新規登録
左メニューから```新規作成```アイコンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/883033de-5e81-e9a7-864c-4445cbcd6c43.png)

#### 名称、クラスの入力
```Name```に名称を入力します。メニュー画面の表示名になります。
```Action```に```Form```を指定します。
```Special Form```に```SimpleForm```を指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/c8a7533c-4dc5-fa4c-f5ef-a7eaed8a4c28.png)

以上で、iDempiereによる設定は完了です。
つづいて、動作確認をします。

## 動作確認
画面上部のツリーアイコンから登録したフォーム「」リンクが表示されていると思います。
```SimpleForm```をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/24493132-42f4-e694-6a89-33870a924511.png)

表示された画面のテキストボックスに値を入力し、エンターを押します。
Eclipseのコンソール画面にデバッグ情報が表示されれば実装の完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3623535/4729435e-60b2-b5f4-de8f-5ff9a039d8c3.png)

## 次回は
今回は、非常にシンプルなフォームを実装してみました。
iDempiereの画面は、zkossを使用しているため次回はzkossのパーツを使用した画面の実装を行いたいと思います。