先日公開した記事では、2回にわたってJavaのWebアプリケーションフレームワーク「Play Framework」でWeb APIを作成する方法をご紹介しました。
今回はJavaScript帳票ライブラリ「ActiveReportsJS(アクティブレポートJS)」を使用して、Play FrameworkのWebアプリケーションから帳票を出力する方法をご紹介したいと思います。
目次
Web APIの作成
事前準備
最初に帳票に出力するデータを返却するWeb APIを作成します。事前準備として、前述のWeb APIの作成方法の記事のうち、前編の手順と、後編の「データベースの作成」までの手順を行っておきます。今回は「ReportingApp」という名前でプロジェクトを作成しました。
H2データベースの設定が完了したら、Ebeanが必要とする「javax-api」を使用するため、「build.sbt」を変更します。
lazy val root = (project in file(".")).enablePlugins(PlayJava, PlayEbean)
・・・(中略)
libraryDependencies ++= Seq(
guice,
jdbc,
"com.h2database" % "h2" % "1.4.199",
// To provide an implementation of JAXB-API, which is required by Ebean.
"javax.xml.bind" % "jaxb-api" % "2.3.1",
"javax.activation" % "activation" % "1.1.1",
"org.glassfish.jaxb" % "jaxb-runtime" % "2.3.2",
)
変更後はコマンドプロンプトより以下のコマンドを実行して、変更を反映させます。
sbt eclipse
モデルの作成
次にEBeanのモデルを作成していきます。最初にEBeanを格納するパッケージを「conf/application.conf」で指定します。
ebean.default="models.*"
次に「app」フォルダの下に「models」フォルダを作成して、そこに新しく「Invoice.java」を追加します。
package models;
import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.validation.constraints.NotNull;
import io.ebean.Finder;
import io.ebean.Model;
@Entity
public class Invoice extends Model {
@Id
public Long id;
@NotNull
public String billno;
public String slipno;
public String customerid;
public String customername;
public String products;
public Long number;
public Long unitprice;
public Date date;
public static Finder<Long, Invoice> find = new Finder<Long, Invoice>(Invoice.class);
}
もし上記のコードを追加したときにエラーが表示される場合は、こちらの記事にあるビルド・パスの構成の手順を行ってください。
コントローラーの作成
次に「app/controllers/HomeController.java」を以下のように修正し、Web APIが実行された際の処理を記述します。
package controllers;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.ebean.Finder;
import models.Invoice;
import play.mvc.Controller;
import play.mvc.Http;
import play.mvc.Result;
public class HomeController extends Controller {
public Result index() {
return ok(views.html.index.render());
}
// 参照
public Result select(Long id) {
Finder<Long, Invoice> finder = new Finder<Long, Invoice>(Invoice.class);
// JSON変換用クラス
ObjectMapper mapper = new ObjectMapper();
String js = "";
if (id == 0) {
List<Invoice> list = finder.all();
try {
// JSON文字列に変換
js = mapper.writeValueAsString(list);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
} else {
Invoice dto = finder.byId(id);
try {
// JSON文字列に変換
js = mapper.writeValueAsString(dto);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
}
return ok(js).as("application/json");
}
// 登録
public Result create(Http.Request request) {
JsonNode json = request.body().asJson();
Invoice dto = new Invoice();
dto.billno = json.get("billno").textValue();
dto.slipno = json.get("slipno").textValue();
dto.customerid = json.get("customerid").textValue();
dto.customername = json.get("customername").textValue();
dto.products = json.get("products").textValue();
dto.number = json.get("number").longValue();
dto.unitprice = json.get("unitprice").longValue();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
dto.date = sdf.parse(json.get("date").textValue());
} catch (ParseException e) {
e.printStackTrace();
}
dto.save();
// JSON変換用クラス
ObjectMapper mapper = new ObjectMapper();
String js = "";
try {
// JSON文字列に変換
js = mapper.writeValueAsString(dto);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return ok(js).as("application/json");
}
// 更新
public Result update(Long id, Http.Request request) {
JsonNode json = request.body().asJson();
Finder<Long, Invoice> finder = new Finder<Long, Invoice>(Invoice.class);
Invoice dto = finder.byId(id);
dto.billno = json.get("billno").textValue();
dto.slipno = json.get("slipno").textValue();
dto.customerid = json.get("customerid").textValue();
dto.customername = json.get("customername").textValue();
dto.products = json.get("products").textValue();
dto.number = json.get("number").longValue();
dto.unitprice = json.get("unitprice").longValue();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
dto.date = sdf.parse(json.get("date").textValue());
} catch (ParseException e) {
e.printStackTrace();
}
dto.update();
return select(id);
}
// 削除
public Result delete(Long id) {
Finder<Long, Invoice> finder = new Finder<Long, Invoice>(Invoice.class);
Invoice dto = finder.byId(id);
dto.delete();
return ok("user deleted");
}
}
routesファイルの変更
Webブラウザからの各種HTTPメソッドの呼び出しに対応できるよう、「conf/routes」ファイルに以下を追記します。
# 参照(全件)用メソッド
GET /Invoices controllers.HomeController.select(id: Long=0)
# 参照(1件)用メソッド
GET /Invoices/:id controllers.HomeController.select(id: Long)
# 登録用メソッド
POST /Invoices controllers.HomeController.create(request: Request)
# 更新用メソッド
PUT /Invoices/:id controllers.HomeController.update(id: Long, request: Request)
# 削除用メソッド
DELETE /Invoices/:id controllers.HomeController.delete(id: Long)
CORSフィルタの有効化
次に異なるドメインからもAPIを実行できるように、「conf/application.conf」ファイルに以下の設定を追加し、CORSフィルタを有効化します。
play.filters.enabled += "play.filters.cors.CORSFilter"
Web APIの実行
実際にWeb APIを実行して動作を確認します。コマンドプロンプトからプロジェクトのフォルダにcdコマンドで移動し、以下のコマンドでサーバを起動します。
sbt run
サーバが起動したら、ブラウザに「http://localhost:9000」とURLを入力します。
画面に表示されている[Apply this script now!]をクリックすることで「conf/evolutions/default/1.sql」にエボリューション・スクリプトが作成されます。
次にPostmanなどのツールを使用してリクエストを送信し、テストしてみます。
POST(登録)
「http://localhost:9000/Invoices」に対してPOSTリクエストを実行してデータを登録します。以下のJSONをBodyに追加して実行します。日付データはUNIX時間で保持しています。
{
"billno": "WS-DF502",
"slipno": "GB465",
"customerid": "1",
"customername": "長崎カントリーフーズ",
"products": "コーヒー 250 ml",
"number": 100,
"unitprice": 100,
"date": "2021-10-05 00:00:00"
}
GET(参照)
「http://localhost:9000/Invoices」に対してGETリクエストを実行します。先ほど登録したデータが取得されます。
PUT(更新)とDELETE(削除)
今回は使用しませんが、「http://localhost:9000/Invoices/1」に対してPUTとDELETEのリクエストを実行することで、更新と削除を行うこともできます。以下の例では「customername」の項目を更新しています。
{
"billno": "WS-DF502",
"slipno": "GB465",
"customerid": "1",
"customername": "長崎カントリーフーズ(更新後)",
"products": "コーヒー 250 ml",
"number": 100,
"unitprice": 100,
"date": "2021-10-05 00:00:00"
}
DELETE(削除)の実行例は以下のとおりです。
テストデータの登録
この後作成する請求書に表示するテストデータとして、あらかじめ以下のJSONデータをWeb APIから登録しておきます。
※ 登録は1件ずつ実行してください。
{
"billno": "WS-DF502",
"slipno": "GB465",
"customerid": "1",
"customername": "長崎カントリーフーズ",
"products": "コーヒー 250 ml",
"number": 100,
"unitprice": 100,
"date": "2021-10-05 00:00:00"
},
{
"billno": "WS-DF502",
"slipno": "GB465",
"customerid": "1",
"customername": "長崎カントリーフーズ",
"products": "紅茶 350 ml",
"number": 300,
"unitprice": 120,
"date": "2021-10-05 00:00:00"
},
{
"billno": "WS-DF502",
"slipno": "DK055",
"customerid": "1",
"customername": "長崎カントリーフーズ",
"products": "炭酸飲料 (オレンジ) 350 ml",
"number": 200,
"unitprice": 120,
"date": "2021-10-09 00:00:00"
}
レポートファイルの作成
次にActiveReportsJSで出力する帳票のレイアウト情報などを含んだレポートファイルを作成します。今回は以下のGitHubで公開しているものを改修して使用します。
なお、レポートファイルの編集にはActiveReportsJSのデザイナのインストールが必要です。無償で使えるトライアル版もございますので、是非お試しください。
GitHubからダウンロードしたファイルから、「reports/Invoice_green_ipa.rdlx-json」をデザイナで開くと、以下のようなレイアウトのレポートファイルが表示されます。
データソースの作成
ダウンロードしたレポートファイルはテスト用の埋め込みのJSONデータを使用しているので、新しくデータソースを追加します。以下のようにデザイナ右側の[データ]タブからデータソースの削除と追加を行います。
※ この操作を行う前に、前半で作成したPlay FrameworkのWeb APIを起動しておいてください。
「データソースの編集」のダイアログが開くので、「データプロバイダ」に「Remote JSON」を選択し、「エンドポイント」に以下のURLを設定し、[変更を保存]をクリックします。
http://localhost:9000/Invoices
データセットの作成
データソースを作成したらデータセットを作成します。デザイナ右側のメニューから作成したデータソースの右側にある[+]アイコンをクリックします。
「新規データセット」ダイアログで、「JSONパス」に以下の値を設定し、[検証]をクリックすると、データベースフィールドが9件作成されます。
$.*
次に、製品の単価と購入数量から購入金額を計算する計算フィールドを追加します。
「計算フィールド」の右側の[+]アイコンをクリックして計算フィールドを追加したら、フィールド名に「price」、値に以下の式を設定し、[変更を保存]をクリックして、ダイアログを閉じます。
{unitprice * number}
日付データの変換
今回作成する請求書に表示する日付データはUNIX時間で保存されているので、Tableコントロールの詳細行の、「日付」の項目に設定されている値「{date}」を以下の式に変更し、ActiveReportsJSがサポートする「ISO 8601」のフォーマットに変更します。
{DateTime.Parse(DateAdd("s", date / 1000, "1970-01-01 00:00:00+00:00"))}
デザイナ上でプレビューの実行
以上でデータソース/データセットの設定は完了です。このレポートファイルはすでにTextBoxなどのレポートコントロールと各フィールドとのバインド設定が完了していますので、このままプレビューが実行可能です。
H2データベースに登録したデータが帳票に表示されるのが確認できます。
帳票出力機能の追加
レポートファイルの改修が完了したら、こちらを使用してPlay Frameworkに帳票出力機能を追加していきます。
静的ファイルの配置
ActiveReportsJSのJavaScriptファイルやCSSファイルなどの静的ファイルを「public」フォルダ配下の「javascripts」フォルダと「stylesheets」フォルダに配置していきます。
「javascripts」フォルダ配下には新しく「locales」フォルダも作成し、ActiveReportsJSのトライアル版、または製品版のZipファイルに含まれる「dist」フォルダから以下のファイルをコピーします。
(※1)印のファイルは、各エクスポート処理を行う場合に必要です。
- javascripts
- ar-js-core.js
- ar-js-viewer.js
- ar-js-pdf.js(※1)
- ar-js-xlsx.js(※1)
- ar-js-html.js(※1)
- javascripts/locales
- ar-js-locales.js
- stylesheets
- ar-js-ui.css
- ar-js-viewer.css
次に同じ「public」フォルダ配下に「reports」フォルダを作成し、先ほど修正したレポートファイル「Invoice_green_ipa.rdlx-json」を配置します。
さらに同じ「public」フォルダ配下に「fonts」フォルダを作成し、GitHubからダウンロードしたファイルから、レポートファイル中で使用しているフォントファイル「ipag.ttf」をコピーして配置します。
※ PDFエクスポートを行わない場合は、フォントファイルの配置は不要です。
Scalaテンプレートの作成
次にPlay FrameworkのビューとなるScalaテンプレートを作成します。「views/index.scala.html」ファイルの内容を以下のように修正し、ActiveReportsJSのJavaScriptファイルやCSSファイルなどの静的ファイルへの参照を追加します。また、divタグで帳票を表示するビューワの領域を定義します。
<!DOCTYPE html>
<html lang="ja">
<head>
<title>ActiveReportsJSビューワ</title>
<link rel="stylesheet" media="screen" href="@routes.Assets.versioned("stylesheets/main.css")">
<link rel="shortcut icon" type="image/png" href="@routes.Assets.versioned("images/favicon.png")">
<link rel="stylesheet" media="screen" href="@routes.Assets.versioned("stylesheets/ar-js-ui.css")" />
<link rel="stylesheet" media="screen" href="@routes.Assets.versioned("stylesheets/ar-js-viewer.css")" />
<script src="@routes.Assets.versioned("javascripts/main.js")" type="text/javascript"></script>
<script type="text/javascript" src="@routes.Assets.versioned("javascripts/ar-js-core.js")"></script>
<script type="text/javascript" src="@routes.Assets.versioned("javascripts/ar-js-viewer.js")"></script>
<script type="text/javascript" src="@routes.Assets.versioned("javascripts/ar-js-pdf.js")"></script>
<script type="text/javascript" src="@routes.Assets.versioned("javascripts/ar-js-xlsx.js")"></script>
<script type="text/javascript" src="@routes.Assets.versioned("javascripts/ar-js-html.js")"></script>
<script type="text/javascript" src="@routes.Assets.versioned("javascripts/locales/ar-js-locales.js")"></script>
</head>
<body>
<div id="ARJSviewerDiv" style="height: 100vh"></div>
</body>
</html>
テンプレートの中で参照している「public/javascript/main.js」の中にビューワの初期化処理を記述します。
document.addEventListener("DOMContentLoaded", function() {
const viewer = new ActiveReports.Viewer('#ARJSviewerDiv', {
language: 'ja'
});
// PDFエクスポートを行う場合はフォントの登録を行う
var IPAGothic = {
name: "IPAゴシック",
source: "/assets/fonts/ipag.ttf"
};
GC.ActiveReports.Core.FontStore.registerFonts(IPAGothic);
viewer.open("/assets/reports/Invoice_green_ipa.rdlx-json");
})
ビルドと実行
以上でビューの設定も完了です。以下のコマンドを実行してアプリケーションを起動します。
sbt run
「http://localhost:9000/」にアクセスすると、以下のようにブラウザ上で帳票が出力されます。サイドメニューからPDFなど各種形式への保存も可能です。
※ インメモリでH2データベースを使用しているので、アプリケーションを再起動した際に、エボリューション・スクリプトの実行とデータの登録が再度必要です。
さいごに
以上がPlay FrameworkでActiveReportsJSを使用して帳票を出力する方法でした。フロントエンドで動作する帳票ライブラリであるActiveReportsJSは様々なWebアプリケーションフレームワークとの連携が可能ですので、気になった方は是非製品Webサイトもご覧ください。
Webサイトでは製品の機能を手軽に体験できるデモアプリケーションやトライアル版も公開しておりますので、こちらもご確認ください。
また、ご導入前の製品に関するご相談、ご導入後の各種サービスに関するご質問など、お気軽にお問合せください。