【Power Apps】業務記録アプリに後から一括登録機能を実装する方法
業務アプリ開発の持論として、「作業が終わったその瞬間にスマホやPCで記録するしかない仕様にする」というものがあります。
しかし、実際の現場はそう甘くありません。 作業中に手が離せなかったり、会議が連続していたりと、**「物理的にスマホを取り出して記録すること自体が不可能」**な場面が多々あります。
結果として、一日の終わりや休憩時間に「記憶を頼りにまとめて入力する」ことになるのですが、Power Appsの標準的なフォームでこれをやろうとすると、「新規作成 → 入力 → 保存 → 新規作成…」を繰り返すことになり、非常にストレスフルです。
そこで今回、**「後からまとめて振り返る」ことに特化した、「買い物かご方式の一括登録機能」を実装しました。 技術的な工夫点、特に「ユーザーの記憶を補助するロジック」**を中心に紹介します。

実装した機能の概要
今回作成したのは、以下のようなUIです。
- 一時保存(コレクション)機能: 1件ずつ送信せず、手元でリストを作成する。
- 時刻の自動連携: 直前のタスクの終了時間を、次のタスクの開始時間に自動セットする。
- 一括送信: 最後にボタン一つでDataverseへ一括登録する。

1. 課題:後から入力する時の「記憶の寸断」を防ぎたい
後からまとめて日報を書く時、一番頭を使うのは**「時系列の整理」**です。 「えーっと、Aの会議が10時に終わって、そのあとBの資料作成をやって…」と思い出しながら入力している時に、毎回時間を手動で設定し直すのは思考のノイズになります。
そこで、**「前の行(タスク)の終了時間を、次の行の開始時間の初期値にする」**というロジックを組み込みました。これにより、ユーザーは作業内容を入れることだけに集中できます。
2. 技術的な実装ポイント
ここからは具体的なコード解説です。 データの格納先にはDataverseを使用し、アプリ内ではコレクション(colWorkLogs)で一時データを管理しています。
① 「直前の終了時間」を取得して次につなげる(AddRecord)
「行追加ボタン」を押した時の処理です。 ここでのポイントは、Last()関数でコレクションの最終行を取得し、その終了時間を次の開始時間の基準にしている点です。
コード
// AddRecord ボタンの OnSelect
// 1. 直前の行の終了時間を計算して取得
Set(
varPrevEnd,
If(
CountRows(colWorkLogs) = 0,
// 初回(0件)の場合は、DB上の最新データの終了時間などを取得
If(IsEmpty(WorkLog_Transaction), Now(), ... ),
// 2件目以降:コレクションの最後の行(Last)を取得
With(
{r: Last(colWorkLogs)},
// 日付と時間を合成してDateTime型にする
r.EndDate + Time(r.EndHour, r.EndMinute, 0)
)
)
);
// 2. 新しい行の開始時間を「直前終了時間の1分後」にセット
Set(
varStart,
DateAdd(varPrevEnd, 1, TimeUnit.Minutes)
);
// 3. コレクションに追加(時・分は分解して保持)
Collect(
colWorkLogs,
{
RowNo: CountRows(colWorkLogs) + 1,
LocalId: GUID(),
StartDate: DateValue(varStart),
StartHour: Hour(varStart),
StartMinute: Minute(varStart),
// ... (その他の項目)
}
);With関数を使うことで、「直前の行(r)」という定義を明確にし、コードの可読性を高めています。これで「A作業が終わったら、即B作業」という連続した入力をスムーズに行えます。
② 安全な一括登録(CreateRecord)
最後に、溜めたコレクションをデータベースに書き込む処理です。 ここでは単に保存するだけでなく、**「データの健全性チェック(バリデーション)」**を行ってから送信しています。
特に重要なのが**「開始時間が終了時間より後になっていないか(時刻逆転)」**のチェックです。
コード
// CreateRecord ボタンの OnSelect
// 1. 未入力チェック & 時刻逆転チェック
If(
CountRows(...) > 0,
Notify("未入力があります", ...),
// 逆転(Start >= End)があるかチェック
With(
{
badRows: Filter(
colWorkLogs As r,
// 開始日時と終了日時を合成して比較
(r.StartDate + Time(r.StartHour, r.StartMinute, 0)) >=
(r.EndDate + Time(r.EndHour, r.EndMinute, 0))
)
},
If(
CountRows(badRows) > 0,
Notify("開始が終了以上の行があります。修正してください。", NotificationType.Error),
// 2. 問題なければ一括登録(ForAll + Patch)
With(
{ toSave: SortByColumns(colWorkLogs, "RowNo", SortOrder.Ascending) },
ForAll(
toSave As r,
With(
{
// ここで最終的なDateTime型を生成
st: r.StartDate + Time(r.StartHour, r.StartMinute, 0),
et: r.EndDate + Time(r.EndHour, r.EndMinute, 0)
},
Patch(
WorkLog_Transaction,
Defaults(WorkLog_Transaction),
{
ActionName: r.ActionName,
StartTime: st,
EndTime: et,
Duration: DateDiff(st, et, TimeUnit.Minutes) // 所要時間も自動計算
}
)
)
);
// 完了後の後始末
Clear(colWorkLogs);
Notify("登録しました。", NotificationType.Success)
)
)
)
);ForAllループの中でもWith関数を活用しています。 st(開始時間)とet(終了時間)を一時変数として定義することで、Patch関数の中が非常にスッキリし、所要時間(Duration)の計算などもしやすくなっています。
おわりに
業務アプリ開発において「UI/UX」というと、見た目の綺麗さが注目されがちですが、本当の使いやすさとは**「ユーザーの業務フロー(文脈)に沿っているか」**にあると思います。
今回は「その場で入力できない」という制約を前提に、「後から思い出しやすい」仕組みをコードで表現しました。 Power AppsのコレクションとWith関数、そしてちょっとしたロジックの工夫で、現場に寄り添ったアプリは作れます。同じような課題を持つ方の参考になれば幸いです。
