PowerAppsコレクションを使って工数実績登録アプリを作ってみた

#はじめに
こんにちは！
PowerAppsのアプリを作りたいですが、素人であった私が機能のいいアプリ作るには工夫しました。
作る途中でまとめた経験などを、ここでみんなとシェアしたいと思います。
みんなが困ったことに、この記事が役に立てるといいです。

#開発環境
アプリ種類：PowerAppsテンプレートアプリ
データソース：関連するExcelファイル

#作成するアプリの概要
今回は工数実績を登録するアプリを作成します。
アプリのインタフェースはこの感じです：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1396815/e2cee56b-9c82-cdf8-f047-9fe938e2dfc2.png)
見た通り、このインタフェースは三つの部分に分けられています。
上から下は、データ登録画面、コレクション一括登録画面、データ一覧画面です。

今度はアプリの作り順番で、コレクション一括登録画面、データ登録画面、データ一覧画面の順番で説明していきたいと思います。


#コレクションで登録機能の作成
まずは、どの機能は、コレクションで作成するのを、見ていきましょう。
図はこのような感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1396815/7c219bd8-d22d-874b-26b0-c8bcc9190dba.png)
これは、一つのギャラリーの中で、コレクションを使用することで複数のレコードを一括登録する機能です。

左の四つのテキストボックスやドロップダウンに、それぞれを”name”、”type”、”memo”、”time”の名前にします、最後登録するときに使います！

左の部分はテキストボックスを追加することで簡単に作れますが、重要なのは右の”＋”と”×”ボタンの機能です。

”＋”ボタンは、左の四つの入力桁を同じもので次の行に追加する機能を持ちます。このボタンの”OnSelect”に書くコードは下記になります：

```
Collect(Workcol,{顧客名:"",案件種別:"AMO",作業内容メモ:"",作業工数:""})
```
Collect関数を使用し、Workcolというコレクションを作ったとのことです。このWorkcolの中には、”顧客名”、”案件種別”、”作業内容メモ”、”作業工数”、この四つの項目があります。

”＋”ボタンを押すと、行が増えていきますので、このアプリに便利な機能を付けました。

”×”ボタンは、×だから削除の意味です。とても簡単なコードでできます（”＋”と同じ”OnSelectに書きます”）：

```
If(CountA(Workcol.案件種別)>1,Remove(Workcol,ThisItem))
```
入力桁は少なくとも一行は保留するので、ある項目の数が1以上であることを条件に入れました。ここで、”案件種別”はドロップダウンで選択、空にはなれないから、”案件種別”の数が1以上という条件に設定しました。

最後に、すべての項目を入力した後、自動的に保存しないから、書いたものを保存するため内容確認ボタンを作りました。
内容確認ボタンのコードは下記になります：

```
ForAll(
    Gallery1.AllItems,
    Patch(
        Workcol,
        ThisRecord,
        {
            顧客名: ThisRecord.name.Text,
            案件種別: type.SelectedText.Value,
            作業内容メモ: memo.Text,
            作業工数: time.Text,
        }
    )
);
Notify(
    "内容を確認しました",
    NotificationType.Success
);
```
ここでPatch関数を使用しましたが、Patch関数は、新規作成とレコード変更両方とも使用できるとても便利な関数です。
Patch関数に関するドキュメントはこちらになります：
https://docs.microsoft.com/ja-jp/powerapps/maker/canvas-apps/functions/function-patch

ボタンを押して、”内容を確認しました”のメッセージが来たら、入力桁に書いたものはコレクションに登録完了です！

#コレクションに登録した内容をソースファイルにアップロード
みんなはもう気づいたと思いますが、先のコレクションに、作業日や、登録時間、登録者などの情報は全然ないよね、そうだったら問題が起こったとき、いつやったの、誰かやったのは全然わからないでしょう。

このアプリでは、一日中一人が作成したものを登録すると定義され、コレクションにあるレコードごとに、作業日や、登録時間、登録者などの情報を全部同じであるはず、と考えました。
だから、最後にソースファイルにアップロードするとき、それらの同じである情報を一括で登録すればいいと思いました。
登録する画面は下記になります：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1396815/1f617206-4c28-b97d-a88c-d2f16f9f0ab9.png)

作業日を選択し、登録ボタンを押せば登録完了です。
登録ボタンの”OnSelect”に書く内容は下記になります：

```
ForAll(Workcol,
      Patch(工数実績,Defaults(工数実績),
      {
       登録者:user.Text,
       'シーケンスNo.':Value(If(Count(Distinct(工数実績,'シーケンスNo.'))=0,"1",Count(Distinct(工数実績,'シーケンスNo.'))=Max(工数実績,'シーケンスNo.'),Max(工数実績,'シーケンスNo.')+1,Min(ForAll(Sequence(Max(工数実績,'シーケンスNo.'),1)As se,If(Last(FirstN(Distinct(Sort(工数実績,'シーケンスNo.'),'シーケンスNo.'),se.Value)).Result<> se.Value,se.Value)),Value))),
       顧客名:顧客名,
       案件種別:案件種別,
       作業内容メモ:作業内容メモ,
       作業工数:Value(作業工数),
       作業日:Text(DataPicker.SelectedDate),
       登録日:Text(
                Now(),
                "[$-ja]yyyy年mm月dd日"
            ),
       登録時刻:Text(
                Now(),
                "[$-ja]hh:mm:ss"
            )
       }
));
Refresh(工数実績);
Notify("新規作成が成功しました",NotificationType.Success);
Navigate(WorktimeEdit);
```

ここに書いてある登録者、シーケンスNo.、顧客名、案件種別などの項目は、必ずExcelのソースファイルにテーブルを作って、テーブルの項目と一致する必要があります。テーブルの名前は、工数実績にしています。

登録者は、登録画面の左上にある黒文字のところ、実はそこのテキストボックスがあります、DisplayModeをDisplayMode.Viewに変更すれば書き換え不能になります。PowerAppsは、Microsoftの製品なので、登録者の名前がわからないが、メールアドレスを使用することが可能です、実際に作る時、user関数を使用して登録者ではなくメールアドレスをあそこに表示するが簡単にできます。

このアプリでレコードごとにシーケン番号があります。シーケンス番号のアルゴリズムについて説明します。
レコードを削除した時、シーケンス番号の連続性を確保するため、次の新規登録するとき使うシーケンス番号は、削除したもののシーケンス番号を補足する。複数削除した場合、削除したものの一番小さいシーケンス番号を取ります。

1.レコードが一つもない時、シーケンス番号を1にする

```
If(Count(Distinct(工数実績,'シーケンスNo.'))=0,"1")
```

2.レコード数は一番大きいシーケンス番号と同じであるとき、レコードは一つも削除されないと考えられ、最大値プラス1の値にする

```
If(Count(Distinct(工数実績,'シーケンスNo.'))=Max(工数実績,'シーケンスNo.'),Max(工数実績,'シーケンスNo.')+1)
```

3.上記の二つの場合以外、削除したレコードがあると考えます。まず、ソースファイルの”シーケンスNo.”列の最大値を取ります、「Max」とします。Sequence関数を使用して、1からMaxまでの連続数字配列を作成します、この配列を「se」とします。ForAllとLast(FirstN())関数を使用して、”シーケンスNo.”とseをトラバースします。違う要数を見つけたら、ForAllの配列に入れます。最後にMin関数を使用して、ForAllの配列の中で一番小さい値を抽出します。

```
Min(ForAll(Sequence(Max(勤務実績,'シーケンスNo.'),1)As se,If(Last(FirstN(Distinct(Sort(勤務実績,'シーケンスNo.'),'シーケンスNo.'),se.Value)).Result<> se.Value,se.Value)),Value)
```

上記三つの条件文で、ようやくシーケンス番号を決めることができました。

作業日は、左のDatePickerで選択します。
登録日と登録日時は、登録ボタンを押すタイミングの、システムの時間にします、Now関数で簡単にできます。
最後に、”新規作成が成功しました”のメッセージが表示したら成功です。

ここで、新規登録に関する機能の紹介が全部完了です。
みんな気づいたかも、登録画面に、”顧客名検索”というテキストボックスがあったよね、使いませんでしたね。
次に紹介します！

#データ一覧表の作成
入力機能だけのアプリは中途半端と思われますね、だから今度のアプリでは、作業日と顧客名情報による検索機能を追加しました。
一覧画面は下記になります：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1396815/13f58256-7431-0697-bce8-a88ed70ed250.png)

作り方はギャラリーにFilter関数を使用します。上記の登録画面で入力した作業日、あるいは顧客名検索のテキストボックスに入力したものと一致するレコードを抽出します。
ギャラリーに入れる関数は下記になります：

```
Filter(勤務実績,Or(DateValue(作業日)=DataPicker.SelectedDate,顧客名=namesearch.Text))
```
Or関数を使用しました。

条件に一致するレコードを抽出したあと、各桁に表示させます。
各項目のしたのテキストボックスに、”ThisItem.項目名”を書けば完了。表示方法はとても簡単です：

```
ThisItem.顧客名
```
```
ThisItem.案件種別
```
```
ThisItem.作業工数
```
```
ThisItem.'シーケンスNo.'
```
今度は、修正する機能も付けましたので（シーケンス番号以外）、修正したいテキストボックスのDisplayModeをDisplayMode.Editに設定すればいいです。
修正したレコードごとに、保存機能を付けました。右の保存機能を示すボタンに、下記のコードを”OnSelect”に書きます：

```
Patch(勤務実績,
     ThisItem,
     {
         顧客名:name2.Text,
         案件種別:type2.Text,
         作業工数:Value(time2.Text),
         更新時刻:Text( Now(), "[$-ja]hh:mm:ss" ),
         更新日: Text( Now(), "[$-ja]yyyy年mm月dd日" )
     }
);
Refresh(工数実績);
Notify("保存しました",NotificationType.Success)
```
またPatch関数を使用しました。今回はレコードの値を新しい値に更新しました。
”保存しました”のメッセージが表示したら完了です。

一番右の”×”ボタンは、コレクションの場合と同じ、削除ボタンです。
しかし、今回は入力桁を一行保留する必要がないから、コードはもっと簡単になりました：

```
Remove(勤務実績,ThisItem)
```
これで、レコードの削除機能の作成は簡単に作成完了です。

#おわりに
1.PowerAppsは確かに簡単にアプリを作れますが、アプリの機能がある程度限られています。
　工数実績、学生の成績登録、店の商売記録などの、簡単な機能を持つアプリは、確かに検討すれば素人でも作れます。
2.アルゴリズムの考え方と書き方は、JavaやPythonなどの汎用言語と違い、最初は慣れませんでした。
3.実際にこのアプリを作ったときに、PowerAppsの作業日はExcelの作業日のフォーマットを統一していなかったから、原因探すにはたくさん時間がかかりました。作る時に、必ず項目名とフォーマットをソースファイルと一致することを確保してください。

最後に、この記事が少しでもどなたかの役に立ったのであれば幸いです。