Google App Makerで社内サービスを作る #3 （スクリプト編）

## App Maker、2021年1月19日に終了

> The App Maker editor and user apps will be discontinued on 19 Jan, 2021. New application creation will be disabled starting 15 Apr, 2020.

記事を書いてる最中に App Maker オワコン化の知らせを聞いてションボリしてました。
が、今回で「社内サービスを作る」シリーズ完結なので、書き上げます。

## やろうとしていること

Google App Maker を使って、社内用の書籍管理アプリを作っています。

### 前回やったこと

前回は、画面を作成しました。

### 今回やること

各種スクリプトを書きます。

## 書籍一覧から詳細画面を開く

書籍一覧画面で書籍を選んだら、詳細画面が開くようにします。

1. 書籍一覧画面の Table の行を選択する
2. Events → onClick を `Show Popup...` → `BookDetail` にする

## 貸し出し履歴を表示する

前回の記事で、書籍ごとの貸し出し履歴を持てるデータソースを作りました。これを正しく設定してテーブルに貸し出し履歴を表示するようにします。

1. 書籍詳細画面の Table を選択する
2. Events → onAttach を `Custom Action` にし、下記を入力する
3. Events → onDetach を `Custom Action` にし、下記を入力する

```javascript:onAttach
var ds = widget.datasource;
ds.query.parameters.BookKey = widget.parent.datasource.item._key;
ds.load();
```

```javascript:onDetach
widget.datasource.unload();
```

前回の記事で `BorrowHistoryByBook` データソースに `BookKey` パラメーターを作りました。
onAttach でこのパラメーターに今選択している書籍のキーを設定します。
書籍のキーは Table 内には無いので、Table の親（ポップアップ）のデータソースから取得しています。

onDetach では貸し出し履歴をアンロードします。
これをしないと一度ポップアップを閉じて別の書籍の詳細を開いた時、前の貸し出し履歴が一瞬見えてしまったため、このようにしています。

## 書籍を借りる処理

書籍を借りる処理をサーバースクリプトで実装します。
画面左側の `SCRIPTS` → `+` を押し、次に `Server` を選択 → `CREATE` します。
これでサーバースクリプトが作成されました。
ここに下記のような処理を書きます。

### サーバー側
```javascript:Server
/**
 * 書籍を取得する.
 *
 * @param {string} key 取得する書籍のキー
 * @param {Book} 書籍
 */
function getBook_(key) {
  var book = app.models.Book.getRecord(key);
  if (!book) {
    throw new Error('no such book key:' + bookKey);
  }
  return book;
}

/**
 * 書籍を借りる.
 *
 * @param {string} bookKey 借りる書籍のキー
 * @param {string} email 借りる人のメールアドレス
 */
function borrowBook(bookKey, email) {
  try {
    app.transaction.cloudSql.start();
    
    // 対象の書籍を取得
    var book = getBook_(bookKey);
    
    // 借りた人と貸出日時を設定
    book.BorrowedAt = new Date();
    book.BorrowedBy = email;
    
    // 保存
    app.saveRecords([book]);
    app.transaction.cloudSql.commit();
  } catch (e) {
    app.transaction.cloudSql.rollback();
    throw e;
  }
}
```

書籍を取得する処理は、書籍を返すときも使いたいため別にしておきました。
関数名に `_` を付けているのは、サーバースクリプトを隠すためです。
https://developers.google.com/appmaker/scripting/server#hiding_server_scripts

### クライアント側

続いて画面から `borrowBook` を実行するスクリプトを書きます。
ポップアップの「借りる」ボタンを選択し、Button → onClick → `Custom Action` を選択して、下記のようなスクリプトを書きます。

```javascript:CustomAction
var bookKey = widget.datasource.item._key;
var email = app.datasources.CurrentUser.item.PrimaryEmail;
widget.enabled = false;

google.script.run
  .withSuccessHandler(function() {
    app.popups.BookDetail.visible = false;
    widget.enabled = true;
    app.datasources.Book.load();
  })
  .withFailureHandler(function(error) {
    alert('エラーが発生: ' + error);
    widget.enabled = true;
  })
  .borrowBook(bookKey, email);
```

Custom Action を入力する欄は、一度に 10 行程度しか表示されないため上記を全部入れると見通しが悪いです。このままでも動作には問題ないですが、気になる場合は別途クライアントスクリプトを作成し、そちらに移すこともできます。

**クライアントスクリプトへ移行する手順**

1. 画面左側の `SCRIPTS` → `+` を押し、次に `Client` を選択 → `CREATE` します。
2. 今作ったクライアントスクリプトに、上記の処理を任意の名前の関数にして保存する
3. 「借りる」ボタンの Custom Action で、↑の関数を呼び出す

これで書籍を借りることができるようになりました。

## 書籍を返す処理

書籍を返す処理を作ります。

### サーバー側
```javascript:Server
/**
 * 書籍を返す.
 *
 * @param {string} bookKey 返す書籍のキー
 */
function returnBook(bookKey) {
  
  try {
    app.transaction.cloudSql.start();
    
    // 対象の書籍を取得
    var book = app.models.Book.getRecord(bookKey);

    // 借りた人とその日時をクリア
    var borrowedAt = book.BorrowedAt;
    var borrowedBy = book.BorrowedBy;
    book.BorrowedAt = null;
    book.BorrowedBy = null;
    
    // 貸出履歴を作成
    var history = app.models.BorrowHistory.newRecord();
    history.BorrowedAt = borrowedAt;
    history.BorrowedBy = borrowedBy;
    history.ReturnedAt = new Date();
    history.Book = book;
    
    // 保存
    app.saveRecords([book, history]);
    app.transaction.cloudSql.commit();
  } catch (e) {
    app.transaction.cloudSql.rollback();
    throw e;
  }
}
```

貸出情報を消しつつ、貸出履歴を作っています。

### クライアント側

ポップアップの「返す」ボタンを選択し、Button → onClick → `Custom Action` を選択して、下記のようなスクリプトを書きます。

```javascript:CustomAction
var bookKey = widget.datasource.item._key;
widget.enabled = false;

google.script.run
  .withSuccessHandler(function() {
    app.popups.BookDetail.visible = false;
    widget.enabled = true;
    app.datasources.Book.load();
  })
  .withFailureHandler(function(error) {
    alert('エラーが発生: ' + error);
    widget.enabled = true;
  })
  .returnBook(bookKey);
```

これで書籍を返すこともできるようになりました。

## 作成完了

これで一通り作成できました。
PREVIEW から動作を確認してみてください。

記事では、書籍や所在地などは登録画面しか作っていません。
余裕があれば、変更や削除する機能も作ってみると良いと思います。

## (´・ω・`)

いまさら、これ読んでくれる人いるかなあ。

## 参考サイト

https://developers-dot-devsite-v2-prod.appspot.com/appmaker/scripting/overview