yukioのアウトプット

Power Apps、Power Automateなど、勉強した内容、実践した内容を、自分にわかるようにかみ砕いてアウトプットしています。

【Power Automate】月の最終営業日にやる仕事を自動化したい(ほぼ完成だがまだ不十分)

先日、同僚からこのような話がありました。

毎月の最終日にお客さんにメールを送る仕事って自動化できないかな?最終日といっても「営業日」だから土日祝があったらその前が最終日になるだろうし、難しいかな?

曜日は曜日番号で判定できるし、祝日の判定等は別の方の記事で見たことがあるので、「多分、頑張ればできると思う」と回答。

同僚の反応は

「頑張れば」か~

という感じでトーンダウン。

そんな同僚を助けてあげようと思ったわけではありませんが、いいネタをいただいたので「どのくらい頑張ればできるのか」を判断するためにもフローを作ってみることに。

結果「結構大変でした・・・(しかもまだ不十分)」

 

(0)おおまかな流れ

土日祝日が休みの企業を想定しているので、最終営業日の定義は「土日でもない、祝日でもない日の中で、その月の最後の日」となると思います(うまく表現できない。言語化難しい・・・)

 

フローの流れとしては、色々試行錯誤した結果、

  • 毎日フローを回す
  • まず、その月の最終日を算出する(土日祝関係なく)
  • その日が土日祝か?を判定して
  • Yesであれば1日前で同じ判定をする
  • Noであれば、その日がその月最後の「土日でも祝日でもない日(=営業日)」となる
  • そうして求まった「最終営業日」と「今日」が等しければメール送信の処理を実行する

こんな形に落ち着きました。

 

以下、フローの作り方をご紹介していきます。

 

(1)トリガー:毎日(今回は検証の都合上手動)

トリガーは「毎日」にしますが、今回は検証作業の都合上手動トリガーにし、入力(引数)として日付情報を入れるようにします。

 

(2)変数の定義

フローに必要な変数を準備します。

 

●最終営業日(候補)を入れる変数

 

●最終営業日(候補)の曜日番号を入れる変数

※後述するdayOfWeek関数を使って曜日に対応した番号を入れるので整数型にしています。

 

●最終営業日(候補)が祝日かどうか?の情報を保存する変数

※後述するOutlookのアクションの返り値を入れたいので「アレイ」型の変数にしています。

JSONなのでオブジェクト型にしたほうが良い気がしたんですが、それだとフローがエラーになるし、よく見てみると実際はJSONが中に入った配列?だった(後述)のでアレイ型にしないといけないのかな?と思われます。

 

●営業日判定用変数(整数型)

※この値を増減させて、土日祝かどうかの判定を行います。

※初期値を1にした理由は後述します。

 

(3)今月末を求める

とりあえず今月の最終日を求めます。

トリガーの日付に対して1か月後を出し、日数を引けば求まります。

土日祝かどうかは、この時点では考慮しません。

(例)トリガーの日付が5月4日の場合

「5月4日」の1か月後は「6月4日」

そこから日数(4日)を引くと「5月31日」 

「時間からの減算」アクションの「間隔」にはこのような式が入っています。

dayOfMonth( )

dayOfMonth関数の公式ドキュメントはこちら

 

(4)求めた「月の最終日」と、その曜日番号を変数へ格納

(3)で求めた「月の最終日」と、その日の曜日番号を変数に格納します。

式には以下が入っています。

(対象日)

formatDateTime( , 'yyyy/MM/dd' )

(曜日番号)

dayOfWeek( )

公式ドキュメントはこちら

formatDateTime関数

dayOfWeek関数

 

(5)月の最終日は祝日か?の情報を取得する

Office 365 Outlookには「日本の祝日」というカレンダーが標準でありますので、「イベントのカレンダー ビューの取得(V3)」アクションを使ってこのような書き方をすると、その日が祝日であれば、祝日関連の情報(配列?)を、祝日でなければ空の情報(配列?)を返してくれます。

 

試しに、2023年12月31日で上記アクションを動かすとこんな配列が出力されます。

中身はJSONですが、先頭に”[” があって、最後に ”]” があるという事は配列?だと思います。

 

(6)JSONを解析して変数に入れる

(5)で取ってきたJSONを解析し、その結果を変数に入れます。

これを噛ませないと変数に入れることができませんでした。

(あれ?配列・・・やっぱりJSONなのかな?)

スキーマのところは、事前に祝日における(5)のアクションだけ別で走らせておいて、祝日情報のJSONをコピーしておき、このアクションの「サンプルから生成」に入れ込んで生成しました。


(7)土日祝かどうかの判定ループ

Do Untilを使って、月末営業日(候補)の曜日及び祝日かどうかの判定を行います。

毎回よく分からなくなるんですが、Do Untilの条件部分には

ループを抜ける条件

を入れないといけません。

(2)で初期化した判定用変数は、後のアクションでも説明しますが、

0であれば「営業日である」

という判断をするため、このような記述にしています。

このループは必ず1回以上は通ってほしいため、変数の初期値が0だとループに入ってくれません。なので初期化の時点で1を入れていたんですね。

 

(7-1)いったん判定用変数をリセット

 

(7-2)曜日を判定する

(4)でdayOfWeek関数を使って出力される曜日番号は

日曜日:0、月曜日:1、・・・、土曜日:6

という風になっています。

この条件では、曜日番号が「0か6のいずれか」であれば「はい」の方に行き、変数の値に1を加える。「いいえ」のときは0を加えます(入れなくてもいい)

 

条件のところの「createAr...」のところには以下の関数が入っています。

createArray(0,6)

createArrayの公式ドキュメントはこちら

以前Hiroさんに教えていただいた内容を応用しました。

 

(7-3)祝日の判定

(5)(6)で変数に保存した祝日情報を使って、祝日かどうかの判定を行います。

 

empty(...)のところには以下の関数が入っています。

empty(  )

祝日であれば、変数「NationalHoliday」には配列で情報が入っており、祝日でなければ何も入っていません。

なのでそれをempty関数で囲んで、それがtrueかfalseかで条件を分けるわけですね。

trueであれば「祝日ではない」ので変数に0を加える(入れなくてもいい)

falseであれば「祝日」ということなので変数に1を加えます。

 

(7-4)変数の結果に応じて処理を分ける

(7-2)(7-3)の処理の結果、対象日が土日・祝日のいずれかであれば、変数「Flag」は1か2になり、そうでなければ0になるはずです。

なので、この条件式を入れて、変数が1以上であれば、1日前にして同じ判定を繰り返します。

0であれば「土日でもなく、祝日でもない」と分かりますので何もせず、Do Untilの条件にも引っ掛かるのでループを抜けることができます。

 

(7-4a)「はい」の場合、最終営業日(候補)を1日前にする

(7-4)が「はい」の場合の処理の中身です。

「時間からの減算」は単純に変数「Day」を1日前にする処理。

次アクション以降は(4)~(6)と同じことをしています。

 

(7-4b)「いいえ」の場合は何もしない

「いいえ」の場合は、既に変数「Day」に最終営業日が入っているので

ループを抜けた後の処理で使います。

変数「Flag」は0のはずですので、ループを抜けることができます。

 

(8)こうして求まった月末営業日と「今日(トリガーの日付)」を比較する

最後の処理です。

条件式のところには

formatDateTime(  , 'yyyy/MM/dd' )

formatDateTime(  , 'yyyy/MM/dd' )

がはいっています。

上記2式がイコールであれば「今日(トリガーの日付)は月末営業日である」ということが分かりますので、「メールの送信」アクションを実行。

イコールでなければ何もしません。

 

(9)実行結果

実際に「2023年12月29日」でフローを走らせるとちゃんとメールが届きました。

 

(10)問題発覚

(9)を書くために、フローの詳細結果を見ているときに判明したんですが

12月29日と30日は祝日ではない。これどうしよう・・・

(上でさぁ、12月29日で最終営業日ちゃんと出とるやん!って書いてるけど、よく考えたら12月の最終営業日って一般的には28日だよねwww)

このように「祝日ではないが一般的に仕事が休み」の場合をどう考慮すべきか?が今後の課題です。

実際に、2024年の12月30日(月曜日)でフローを走らせると、メールが送信されてしまいました。

今後改善案が思いつけば試して、うまくいけばブログに追記したいと思います。

 

 

 

【Power Apps/Power Automate】社用車の車検時期を管理できるようにした話

自分の勤め先でPower Platformが使われ始めて約1年。

まだ興味を示したり、開発に着手してくれる社員はいませんが、「こんなものがあるんだ」というのは社内に浸透してきた矢先、役員からこのような相談がありました。

 

うちの社用車の車検手配(今までは総務部長1人でやっていた)を、人が減って総務の業務負担が増えてきたので、各車両の担当者が管理する運用に変えたい。うまいこと通知したり、アプリで見える化したりできんかな?

 

そのぐらいならできそう!と思ったので

「出来ると思います。作ってみます!」

と即答。2週間ぐらいでサクサクっと仕上げることができましたので、その詳細をまとめようと思います。

 

(1)今まで管理に使っていたExcelファイルを精査

今まではExcelのファイルで管理していたようだったので、それをもらって眺めてみました。

(こういう時に、Excelファイルくれるだけでいいのに、わざわざ印刷してくれるあたり、何だかなぁって思いますwww)

入っていた情報は以下の11種でした。

  1. 車名
  2. 車のナンバー
  3. 購入年月日
  4. 購入年月日(会社的に何期で買ったか)
  5. 新車で買ったか中古で買ったか
  6. その車両の担当者
  7. 車検満了日
  8. 給油所
  9. 車検依頼先
  10. 車検依頼先(電話番号)
  11. オイル交換依頼先

このうち、「3.と4.は一緒の列(年月日だけ)で良いかな?」「新車か中古車かとかいらなくね?」などなど検討を進め、SharePointリストに移植する準備を行いました。

 

(2)SharePointリストの作成

次に、このExcelをSharePointリストに移植しました。

以下は、自分の開発者プログラムで作ったリストなので、車のデータをダミーにしています。

作った列は以下の通り。最初はここまでたくさん作りませんでしたが、アプリやフローを作っているうちに必要と判断して追加した列も含みます。

  1. Car(1行テキスト):車種
  2. Title(デフォルトである1行テキスト):車のナンバー
  3. Category(選択肢):普通車か軽自動車か
  4. Owner(ユーザー):車両担当者
  5. MaintenanceLimit(日付と時刻):車検満了日
  6. Store(1行テキスト):車検依頼先
  7. StoreTEL(1行テキスト):車検依頼先の電話番号
  8. Yoyaku(はい/いいえ):予約したかどうかの状態を保管
  9. Buy(日付と時刻):その車両の購入日
  10. Fuel(1行テキスト):給油先
  11. Oil(1行テキスト):オイル交換依頼先
  12. Others(複数行テキスト):備考

 

(3)アプリトップ画面

アプリのトップ画面はこのような形です。

先ほど作ったSharePointリストの中身の一部をギャラリー表示しただけですね。

 

「車検満了日」のテキストラベルは、Colorプロパティを

If(
    Value(ThisItem.MaintenanceLimit-Today())<30,
    Color.Red,
    Color.Black
)

FontWeightプロパティを

If(
    Value(ThisItem.MaintenanceLimit-Today())<30,
    FontWeight.Bold,
    FontWeight.Normal
)

このようにして、車検まで30日を切ったものは「赤い太字」で表示されるようにしました。

それぞれの「見る」ボタンを押すと、後で紹介する「DetailScreen」に遷移するようになっています。

OnSelectには次のような関数を入れています。

Set(gblThisItem,galCar.Selected);
Navigate(DetailScreen)

次の画面で扱うために変数にレコードを入れてますが「ギャラリー.Selected」でどうにかしてもいいと思います。

 

(4)「見る」ボタンを押した先の画面「DetailScreen」

トップ画面のギャラリー内「見る」ボタンをポチっとすると、このような画面に遷移するようにしました。

 

車検期限が記載されたテキストラベルは、先ほどと同じように30日を切ったら「赤・太字」で表示されます。

 

「車検を依頼した」ボタンは、DisplayModeプロパティに

If(And(Value(gblThisItem.MaintenanceLimit-Today())<45,gblThisItem.Yoyaku=false),
    DisplayMode.Edit,
    DisplayMode.Disabled
)

を指定しているので、車検まで46日以上余裕がある場合や、既に予約をした場合は押せないようになっています。

 

車検が近づいてくると、Power Automateが通知してくれる(後述)ので、ユーザーには、車ごとに指定された依頼先に車検を依頼した後このボタンを押してもらいます。

ボタンのOnSelectはこのようにしています。

Patch(
    CarMaintenanceList,
    gblThisItem,
    {
        Yoyaku:true
    }
);
Refresh(CarMaintenanceList);
Navigate(MainScreen);

Patch関数を使って、Yoyaku列だけ更新します。

詳しくはPower Automateフローの紹介時に記述しますが、これで「予約したよ!」状態として記録することができます。

 

車検が終わったら、画面下部の日付選択コントロール

に日付を入れて、「車検が終わった」ボタンを押したら期限が反映されます。

一般的に商用車の車検は、普通車の場合は1年に1回、軽自動車は2年に1回なので、日付選択コントロールのDefaultDateプロパティに

If(gblThisItem.Category.Value="軽自動車",
    DateAdd(gblThisItem.MaintenanceLimit,2,TimeUnit.Years),
    DateAdd(gblThisItem.MaintenanceLimit,1,TimeUnit.Years)
)

を入れておき、MaintenanceLimit列に指定してある日付の1年後や2年後がデフォルトで日付選択コントロールに入るようになっています。

これによりユーザーは、このコントロール周辺で何回もポチポチして期限を設定しなくてもよくなり、操作数が減ります。

仮に、上記関数で動的に設定した日付でなかったとしても、大まかには合っているので、数クリックで期限を設定することができます。

「車検が終わった」ボタンのOnSelectはこちらです。

Patch(
    CarMaintenanceList,
    gblThisItem,
    {
        MaintenanceLimit:dteMaintenanceLimit.SelectedDate,
        Yoyaku:false
    }
);
Refresh(CarMaintenanceList);
Navigate(MainScreen);

同じくPatch関数で期限を更新して、Yoyaku列をfalseに戻しています。

 

(5)鉛筆マークを押したら出てくる画面「EditScreen」

DetailScreenの右上の鉛筆マーク

をクリックすると、詳細な項目を編集する画面に遷移します。

 

画面はこんな感じ

これはただ単純に編集フォームをどーんとのっけただけです。

自動生成アプリで作られる画面と大差ないので、詳細な紹介は省きます。

 

アプリの説明は以上になります。

 

(6)Power Automateで通知するフロー

ただ、こうしてアプリで見える化するだけでは、人手不足で忙しい弊社の社員は必ず車検依頼を忘れます。

そこで、Power Automateにて、車検が迫ったものに関しては、担当者にTeamsで通知するフローを組みました。

全体像はこんな感じ。

小さくてよく分からないかもしれませんwww

詳しく見ていきます。

 

(6-1)トリガー:毎日朝9時10分

毎日動かしたいので、1日ごとに動いてもらうようにします。

将来的に、土日祝日の場合を考慮した形にできないかな~と検討中です。

 

(6-2)現在の時刻を取得し、タイムゾーン変換(ついでに表示形式調整)

「yyyy/MM/dd」の形の日付情報が欲しかったので、この2つのアクションを挟んでいます。

 

(6-3)社有車のリストから全件取得

SharePointの「複数の項目の取得」アクションを使って、社用車のリストから全件取得してきます。

※このリストは10件程度の少ないリストなので、躊躇なく全件取得していますが、巨大なリストでこれをやるとおかしなことになるかもしれません。

「フィルタークエリ」のところに書いている「ID gt 0」の詳細はこちらのブログで説明しています。

yukio-ms365.hatenablog.com

 

 

(6-4)Apply to each

さっき取ってきた項目でApply to eachを回します。

以降はApply to eachの中身の話になっていきます。

 

(6-5)回している対象をピンポイントで取得

なんか回りくどいアクションを1個挟んでいる気になっていますが、なぜかこれを入れないとうまく動作しないのでこうしています。

 

(6-6)車検まであと何日かを計算する

関数を使って、車検まであと何日か?を計算します。

関数はこのように入れています。

 

ticks関数などを駆使していますが、こちらは過去のブログに書いた(MicrosoftMVPのMiyakeさんに教えてもらった)方法を応用しています。

yukio-ms365.hatenablog.com

 

 

(6-7)残り日数によって場合分け

先ほど計算で求めた残り日数が「45日」「30日」「15日以下」のいずれかに当てはまったら処理を実行したいので、次の「条件」アクションを入れました。

左側に見える「createAr・・・」の部分には以下の関数が入っています。

createArray(45,30,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1)

この方法はMicrosoftMVPのHiroさんのブログで紹介されています。

過去の私の質問を記事にしていただいたもので、それを応用しました。

mofumofupower.hatenablog.com

 

このようにすることによって、createArrayの中に入れている数値の中に、残り日数が含まれている場合は、この条件がtrue(はい)となり、左側の処理に行くことになります。

false(いいえ)の場合は何もする必要がないので空欄にしています。

 

(6-8)(6-7が「はい」の場合)車検予約したかどうかを見る

ここで(4)の「車検を予約した」ボタンを押したかどうか?(Yoyaku列がtrueかどうか?)を見て、false(いいえ)の場合にのみ、最後のTeams通知の処理を実行し、true(はい)の場合は何もしません。

車検予約したのに「車検予約しろよ~」という通知が繰り返し来ても迷惑ですからね。

 

(6-9)(6-8が「いいえ」の場合)メンショントークンを取得し、Teamsに通知する

最後の処理です。その車両の担当者のメンショントークンを取得し、Teamsのチャットにメンションを付けて投稿します。

Owner列がユーザー列なので、その中身の「Email」を指定することでメンショントークンを取得することができます。

 

(6-10)フロー完成

フローが完成しました。

毎日フローが走り、車検が迫った車が見つかると、担当者にこのような通知が来るようになります。

この通知は「あと15日」の段階ですが、45日の時と30日の時、そして14日~1日でも通知が送信されるようになっています。

ですが、気づいた段階できちんと車検を依頼して、その後アプリを開いて「車検を予約した」ボタンを押しておけば、Yoyaku列がtrueに更新されるので、それ以降に走るフローでは(6-8)の条件で除外され、通知は届かなくなります。

 

(7)まとめ

この程度の仕組みだったらすぐ作れるようになってきました。

ざっくりとまとめてみましたが、「ここ分かりにくい」「この辺りもうちょい詳しく」等ありましたら、この記事のコメントか、Twitter(@yukio_365)のDM等でご指摘いただければ、追記・編集したいと思います。

 

ブログ書いていく中で、過去のブログでアウトプットした内容や、過去に教えてもらった内容を存分に活かして作っていたことが分かり、改めてアウトプットの重要性を実感しました。

 

【Power Automate】Excelの「行の取得」のキー値に日付を使う方法

(この記事は、ある方のツイートとそれに対するMVPからのアドバイス、それを拝見した第三者の私が、アドバイスを紐解いて、自分の備忘録として書きなぐっている記事です。

読みやすい記事ではないかもしれませんがご容赦ください。)

 

少し前にこんなツイートを拝見しました。

 

Power Automate(クラウドフロー)にある、Excelの「行の取得」アクション。

キー列とキー値を設定して、それに合致する行を取得するというものですが、

まるさんは、この「キー列」に日付を設定した時に、うまく行が取得できずに悩まれている様子です。

 

Excelのセルに日付が入力されていても、本当は日付情報ではなくて「シリアル値」で入力されているため、Power Automateの「行の取得」のキー値に「日付情報」を入れてもうまく取得できないという感じでしょうか。

 

私がやっていること

私も同じようなシチュエーションで行を取得したい用事がありました。

その時は以下のようなことで対応しました。

「フローの可読性」といった意味ではいいかもしれませんが、

余計な列を1個作ることになり、なんとなく「スマートじゃない」と思っています。

 

まるさんからは「賢い」とお褒めの言葉を頂きましたが、この方法を取った理由は単純に

シリアル値で考えるのが面倒臭い

と思ったからでした(苦笑)

当時の自分に喝!

 

MVPからのアドバイス

まるさんのツイートに対して、今年Microsoft MVPを受賞されたMiyakeさんから、以下のようなアドバイスがありました。

 

ほうほう、何やら難しいことをやってるぞ!

ちょっと紐解いてみよう!

 

式を見てみると

・ticks関数

・sub関数

・div関数

・add関数

の4種類を使っています。

それぞれの部分で何をしてるかを分析してみたので、自分の備忘録として、以下にまとめてみようと思います。

 

(1)ticks関数

入れ子になっている関数の一番内側「ticks関数」から見ていきます。

公式ドキュメントは以下です。

learn.microsoft.com

 

これによると、ticks関数というのは

1 月 1 日 0001 12:00:00 午前 0 時から指定したタイムスタンプまでの 100 ナノ秒間隔のティック数 (または C# の DateTime.Ticks) を返します。

との事です。

は???

 

でもゆっくり読んでみると何となくわかりました。

 

西暦1年1月1日午前0時00分00秒を始点として、

そこから、

指定されたタイムスタンプまでの秒数を

ナノ秒に変換して

100で割った値を返す。

(↑「100ナノ秒間隔の」と書いてあるので)

 

ということだと思います。

 

(1)ticks関数検証

検証のためにこんなフローを組んでみました。

 

1個目は「西暦1年1月1日0:00:00」

2個目は「その1秒後」

ですね。

 

実行結果はこうなりました。

 

1個目は私の思った通り「0」が返ってきました。

2個目で返ってきた値は「10000000」つまり1000万

 

1秒をナノ秒になおすと「1000000000(10の9乗=10億)」ナノ秒で、

それを100で割ると「10000000(1000万)」

思った通りです。

 

まあ、回りくどい検証をしましたが、

ticks関数はそういった値を返す関数ということです。

 

(2)sub関数

次にやっていることを見るために、式の一部を抜粋すると

sub(ticks('1/6/2023'),ticks('1/1/1900'))

ticks関数で求めた「2023/1/6の値」と「1900/1/1の値」をsub関数で囲っています。

 

sub関数の公式ドキュメントは以下です。

learn.microsoft.com

sub関数は単純に「引き算の関数」ですね。

 

sub(ticks('1/6/2023'),ticks('1/1/1900'))

ということは、(1)で求めた

2023年1月6日の値(ナノ秒÷100)

引く

1900年1月1日の値(ナノ秒÷100)

ということになります。

Excelの「シリアル値」は1900年1月1日を基準にした値なので、

1900年1月1日からどのくらい遠いのか?

を知る必要があり、この引き算を入れているということみたいです。

 

(3)div関数

もう1個外側の関数まで広げて見ていくと

div(sub(ticks('1/6/2023'),ticks('1/1/1900')),864000000000)

subまでで求まった値と864000000000を、div関数で囲っています。

 

div関数の公式ドキュメントは以下です。

learn.microsoft.com

div関数も単純で「割り算の関数」です。

 

div(sub(ticks('1/6/2023'),ticks('1/1/1900')),864000000000)

ということは、

「(2)までで求まった値」÷「864000000000」

をしているということになります。

 

(3)なぜ864000000000?

1日は24時間

1時間は60分

1分は60秒

なので、1日は24×60×60=86400(秒)です。

これをナノ秒に直すと86400000000000

100で割って864000000000

 

これで割ることによって、

単位が[ナノ秒/100]から[日]になり、

1900/1/1から何日たっているか?を知ることができる。

ということみたいですね。

 

(4)add関数

入れ子の最後は

add(div(sub(ticks('1/6/2023'),ticks('1/1/1900')),864000000000),2)

add関数です。

 

add関数の公式ドキュメントは以下です。

learn.microsoft.com

add関数は「足し算の関数」です。

 

といことは(3)までで求まった値に2を足しています。

なぜ?

ここが分からなかったので、ツイートにリプライして聞いてみました。

すると、以下のような返答を頂きました。

 

なるほど、この2つの事情があるがために、

add関数を入れないと2日足りない状況が出来上がるんですね。

 

実際のフローで使うときはどうするか

ここまでは「2023/1/6」という固定の日付でやってましたが、これを実際のフローで動かす場合は、このような形をとるのかなと思います。

肝心の式の部分はこのような入れ方になります。

今回はテストなので、手動でフローを実行した時の時刻をタイムゾーン変換して使いましたが、これが定期的に実行するフローであれば、その時刻になるだろうし、別の方法で日時を取ってくるとか、色々あると思います。

 

まとめ

ここまでちょこちょこ貼り付けた、Power Automateの関数が解説されている公式ドキュメント。

トップページは以下です。

learn.microsoft.com

 

私はこのページをブックマークして時々見ていますが、それだけじゃなかなか定着には至らなくて、今回、一つの事例に対して、頂いたアドバイスを少しずつ紐解いてみたら、より深くPower Automateの関数について知ることができました。

 

きっかけを与えて下さったまるさん

アドバイスを頂いたMiyakeさん

ありがとうございました。

 

(2023/01/10追記)

この内容について、MiyakeさんがQiitaに分かりやすくまとめて下さいました。

qiita.com

合わせて読むと理解が深まりますので、ぜひご覧ください。

 

【Power Automate】SharePointリストの「フィルタークエリ」でハマって、コミュニティの皆様に助けて頂いた話。

2022年12月14日(水)、Microsoft Power Automate Advent Calendar 2022(カレンダー2)用

 

2日連続、アドベントカレンダー用に記事を書いております。

どうもyukioです。

 

私の勤め先では、Microsoft365が導入されており、SharePoint、Teamsなどは使える環境にあるので、Power Appsを使った簡単なアプリと、Power Automateを使った簡単なフローを構築しています。

今回はその開発をする中で、ちょっと扱いに困った現象と、それをコミュニティで相談して助けて頂いた話をご紹介します。

 

SharePointリストで「社員マスタ」を作った

Power Appsでアプリを開発して、夏から社内で運用しているんですが、毎日アプリを開いて入力してほしい業務だったので、Power Automateを使って、毎日「入力して下さーい」と、Teamsで通知するフローを作ろうと考えました。

アプリを使う人は全員ではないため、SharePoint Onlineのリストに「社員マスタ」を作り、「はい/いいえ」列で、通知を出す人と、出さない人を区別することにしました。

その「社員マスタ」。

はじめから、アプリを使用する人だけしか存在しないリストにしても良かったんですが、今後違うアプリを作ったときに汎用性がきくように、全社員を登録した社員マスタにしました。

※画像はイメージです。

 

Power Automateでフローを作るとき

上記のリストから「通知したい人」すなわち「Notice」列にチェックが入っている人に等しい人を取ってくるときには、SharePointコネクタの中にある「複数の項目の取得」アクションを使います。

「Notice」列が「true」の社員だけを抽出しようと思うと、普通「フィルタークエリ」はこのような形になるはずです。

「eq」は「equal」の略で「等しい」

「Notice」が「true」に「equal(等しい)」。

理論上はこれで正しいはずですよね。。。

 

そして、取ってきた人がひと目でわかるように、先ほどの「複数の項目の取得」の続きにこんなアクションを入れてみました。

 

さて、リストをもう1回見てみましょう。

「Notice」が「true」に等しいユーザーなので

・yukio

・ブラームス

・ベートーヴェン

・阿井 上男

の4名が文字列変数に順番に追加されて行って、Teamsに通知されてくるはずです。

ところが、先ほどのフローを実行してみると・・・

 

なぜか、「Notice」列が「false」である

・チャイコフスキー

・ドヴォルザーク

が取得されてしまいました。

 

逆じゃね!?

 

試したこと(1)とりあえず逆にしてみる

とりあえず、「フィルタークエリ」をこのようにしてみました。

真逆にしてみました。

「ne」は「not equal」の略なので

「Notice」列が「true」と「not equal(等しくない)」

これで実行してみると・・・

 

trueの社員が抽出されました。

逆じゃね!?

 

仕方ないのでこれで運用していたが・・・

「逆じゃね!?」と思いつつも、「Notice ne true」で意図する人を抽出することができるので、これで運用していました。

でも、どうにも気持ち悪いし、もし将来的にこの問題が修正されると、全く違う人に通知が言ってしまったりして怖いので、いつも参加させて頂いているコミュニティ「気ままに勉強会」で質問することにしました。

現象を見せてみたところ、何名もの参加者様から「こうしてみたら?」「ああしてみたら?」というアイディアが続々と!

その場で1個1個試していきました。

ここの参加者の方は、分からない問題に対してとことん検証してくれる人ばかりで非常にありがたいです。

以下に、あの場で試した内容を再度自分で試してみた結果を載せていきます。

 

※聞いたことの記憶、自分のメモ、当時のTeams会議チャットを基に書いています。「あのときこれ試したのに書かれてねえじゃん!」ってことがあるかもしれません。そこはご了承ください。

 

試したこと(2)シングルクォーテーションで囲む

フィルタークエリに文字列の列を使って、「この文字列に等しいとき」等に使うシングルクォーテーションを付けてみました。

こんな感じですね。

 

では実行結果を見てみましょう。

やっぱり逆。

シングルクォーテーションはそのままに、「eq」を「ne」にすると、意図するもの(trueの人)が抽出されました。

やっぱり逆。

 

試したこと(3)「true」の部分を式で入れてみる

「true」と直打ちするのではなく、「式」から「true」を入れてみました。

 

「複数の項目の取得」アクションはこのような見た目になります。

 

では実行してみます。

フローが失敗しました。

この書き方は正しい書き方ではないようです。

 

試したこと(4) (3)で入れた「true」の前後にシングルクォーテーション

次にご指摘いただいたのは、先ほどの「式で入れたtrue」の前後にシングルクォーテーションを入れるという方法です。

見づらいので拡大表示。こんな感じですね。

 

これで実行してみると・・・

やっぱり逆の結果になりました。

「eq」を「ne」に変えるとtrueの人が抽出されます。

うーん、やっぱり逆。

 

試したこと(5)1と入れてみる

次のご指摘が目から鱗でした。

「trueかfalse」ということは、2進数で言うと「1か0」、ということは↓↓↓

このようにしてみたら?というご指摘です。

 

試してみると・・・

おっ!trueの人が抽出された!

 

結論

SharePointリストでは「はい/いいえ」列をフィルタークエリに使わない運用方法にすべき!

例えば、

・数値列にして、通知してほしい人に「1」、そうじゃない人に「0」とするとか、

・選択肢列にしてみるとか

こんなところかなぁと思います。

 

・・・と、ここまででブログは締めようかなと思ったんですが、ふと

数値列と選択肢列、試してねえやん・・・

と思ったので、試してみて結果を続きに書くことにしました。

 

試したこと(番外編1)数値列

先ほどのリストに、数値列「Notice_Number」を作成し、

「通知したい人」に1、「通知したくない人」に0を入れました。

↓↓↓

 

そして、Power Automateの方の「フィルタークエリ」はこのように。

↓↓↓

 

では、実行してみます。

うん、ちゃんと、「Notice_Number」を「1」にした人のみが抽出されました。

 

試したこと(番外編2)選択肢列

今度は、選択肢列「Notice_Choice」を作り、「通知する」と「通知しない」の2つの選択肢を設けてみました。

↓↓↓

 

フィルタークエリはこのようにしました。

↓↓↓


実行結果はこちら

こちらもきちんと「通知する」にした人が抽出されました。

 

さいごに

今回は、Power AutomateでSharePointリストのデータを扱うにあたり、ちょっと困った現象をお話ししました。

今後ともコミュニティで勉強した内容や、助けて頂いた事や、自分で試したことをブログにまとめていきたいと思います。

 

(2023/02/12追記)番外編の番外編「falseを基準にする」

2023年1月、takmasさんがQiitaにて、このテーマに関する記事を投稿されていました。

その中に、本ブログでは試してない内容がありました。

qiita.com

 

本ブログ冒頭、そして(1)では「trueを基準」に考えてフィルタークエリを書いていましたが、それを「falseを基準」にするというものです。

 

試したこと(番外編の番外編1)「ne false」

trueの人を抽出したいので、フィルタークエリはこのような形になります。

「ne false」なので「falseでない」

つまり「true」

つまり「チェックがついている人」

が抽出されてくるはずです。

 

フローを走らせてみます。

ちゃんと「trueの人」が抽出されました!

 

試したこと(番外編の番外編2)じゃあ「eq false」ではどうなるか?

じゃあ、その逆だとどうなるか試します。

 

さっきの逆ですね。

理論上は「falseの人」が抽出されてくるはずです。

ではフローを走らせます。

ちゃんとfalseの人が抽出されましたね。

 

他の方が紹介されていた内容でも「ああ、そうなのね」だけじゃなく、自分で試してアウトプットすることで、定着につながったような気がします。

【Power Automate】「私用があるので残業(休日出勤)しません!」という無言の意思表示を半自動化してみた。

2022年12月13日(火)、Microsoft Power Automate Advent Calendar 2022(カレンダー2)用

 

最近はConnpassを中心に展開されているコミュニティイベントに参加したり、ありがたいことに登壇の機会を頂いたりしております。

どうもyukioです。

 

私はブログ執筆当時、会社からそこそこ離れたところに住んでいて、自家用車で通勤していました。(現在はもうちょっと近いところに引っ越しました。)

そのうえ、帰り道は車が多くて渋滞するんですよね。

ちょっと残業になって会社出発が18時台になってしまうと、帰宅は平気で19時超え。

なので、平日の夜(19:00とか19:30とか)に開催されるイベントに参加しようと思うと、なるべく定時で帰りたいところ。

あとは、土日にもイベントがあるので、それに参加する場合は休日出勤したくないですよね。

 

私の現在の予定は、

・私用を管理しているGoogleカレンダー(無料のGoogleアカウント)

・仕事の予定を管理しているOutlook(会社支給のMS365アカウント)

を別々で管理しています。

 

今までは

「この時間は私用が入っているので残業できません」

「この日は私用が入っているので休日出勤できません」

という無言の意思表示をするために、

仕事用のOutlookに「私用」という名前の予定を手動で追加していました。

 

でも

・私用Googleカレンダーに予定を入れる

・仕事用Outlookに予定を入れる

という二重の作業になって非常に面倒なんですよね。

 

でも、ある日ふと

「Power Automateでフロー組んだら、Googleに入れるだけでOutlookに自動で入れられるんじゃね?」

と思い立ち、フローを作ってみたところ、

思いのほか簡単にちゃちゃっと半自動化が実現できました!

ですので、その方法をまとめようと思います。

 

 

フロー全容

フローの全容はこんな感じです。

 

単純に

「Googleカレンダーに予定が追加された」

ことをトリガーとして、

いろいろ処理をして、

その予定をMS365のOutlookにも入れる

だけですね。

 

ぶっちゃけ、この程度の仕組みだったら、いろんな方が作られていますので、

「何番煎じなんだよ!」

な話ですが、ちょっとした「自分なりのこだわり」も入ってますし、自分のアウトプットとしての記事なので紹介させてください。

 

(1)トリガー:イベントがカレンダーに追加されたとき

トリガーはこちらです。

Microsoftなのに、Googleなどの外部のサービスと連携することができる。

ほんと開発者さんに感謝ですね。

 

(2)タイムゾーンの変換

取ってきたイベントの「開始時刻」と「終了時刻」を取ってきて、タイムゾーンを日本時間に変更しておきます。

これは別にしなくても問題ありません。

入れなかった場合と入れた場合でどういう違いが出るのか?は後述します。

 

(3)イベントの時間を延ばす

ここが私のこだわりです。

さっきタイムゾーン変換した開始時刻を「1時間前」に、終了時刻を「1時間後」にして、私用としての「尺」を長く見せる処理をしています。

これで「●時(開始の1時間前)までに帰る!」という無言の主張になり、余裕をもってコミュニティイベントに臨めるということですね。

 

(4)Outlookに予定を入れる

あとは、こうして出来上がった情報を、Outlookに入れていくだけです。

「件名」に、Googleカレンダーからとってきた「イベントタイトル」を入れることもできますが、「どんな内容の私用なのか」を会社の人に知られたくなかったので、一律に「私用」という件名で登録することにします。

※画像下部の「詳細オプションを表示する」を開いていろいろやったら、「非公開の予定」として登録することもできます。

 

実行結果

さて実行してみます。

Googleカレンダーでは↑↑↑のような形で入れた予定が・・・

 

Outlookには↑↑↑のような形で入りました。

午後7時から8時 → 午後6時から9時

にちゃんと変換され、タイトルも「私用」として入っていますね。

 

「タイムゾーンの変換」を挟む理由は?

最初フローを作ったときは、「タイムゾーンの変換」を入れていませんでした。

それでも、予定画面での映り方含め、ほとんど問題ありませんでした。

でも、一部問題がありましたので、この件について触れたいと思います。

 

まずは先ほどと同じように、「午後7時~8時」で予定を入れてみます。

 

↑↑↑Outlookに問題なく入りました。

 

↑↑↑先ほどの画面はブラウザ版です。問題だったのはデスクトップアプリのOutlookでした。

この表示は問題なかったものの、ダブルクリックして編集をしようとすると・・・

 

↑↑↑あれ?時刻がずれている?

どうやら、Googleカレンダーから「イベント開始日時」や「イベント終了日時」を取ってくるだけでは、「協定世界時」として認識するようでした。

見た目上は問題ありませんでしたが、この現象がどうも気持ち悪いので、「(2)タイムゾーンの変換」を手順として入れたという訳です。

 

さいごに

今回は、Power Automateを使ったちょっとした小ネタをご紹介しました。

最初に作った「当番通知フロー」のシリーズ連載以降、ブログ執筆から遠ざかってましたが、Microsoft Power Automate Advent Calendar 2022がはじまったのを機に、自分を奮い立たせて執筆させていただきました。

今後も、自分のアウトプットとして色々書いていきたいと思います。

 

実は、Microsoft Power Automate Advent Calendar 2022、明日(12/14)も登場予定です!

ぜひご覧いただければと思います。

 

 

 

 

【PowerAutomate】週替わりの当番を自動でTeamsに通知する(2-5)~(3)フロー作成続き

※2022年8月時点の情報です。

 

yukio-ms365.hatenablog.com

こちらの記事の続きになります。

長らく放置してしまいましたが、続きを書いていきたいと思います。

 

(2-5)次の当番を計算する

いよいよ、取得してきた「今週の当番」から「来週の当番」を計算する部分に入ります。

 

(2-5-1)一時変数「tmp」に今週の当番IDを入れます。

「変数の設定」アクションを使います。

自動的に「Apply to each」で囲まれてしまいましたが、さして問題ではありません。

 

(2-5-2)計算する

 前回、(2-3-2)で初期化した変数(まずは「t1」)に、計算結果を保存していきます。

 ※3人ローテなので、(2-5-1)(2-5-2)を3回分行います。

 

ここで、計算手順の解説

計算の手順は次のとおりです。

①今週の当番のIDを保存

②1を引いて「0始まりの連番」にする

③3人ローテなので3を足す

④人数(tn)で割った余りを求める

⑤1を足して「1始まりの連番」に戻す

さきほどの「変数の設定」の中で、上記②~⑤を、1つの式に詰め込んでいます。

 

なぜこのような細かい手順を踏んでいるかと言いますと・・・

「条件分岐を使いたくない」という変なこだわりがあったからです(笑)

「じゃあ3足すだけでよくね?」と思ったんですが・・・

この画像の黄色い部分にあるように、

単純に3足すだけだと、ローテ人数より多い数字が出てしまうときがあります。

 

ここで私はなんとな~く

「3足した後に人数で割って余りを出したらよくね?」

と思いました。

ですが・・・

この図のように「余り0の時にどうするか」問題が出てきます。

 

試行錯誤の結果、この手順にたどり着いたわけです。

 

ある程度勉強が進んできた今、このブログを書きながら思っていますが、

こんなややこしいことをしなくても、

「3足した結果を見て、人数以上の値が出てきたら引き算する」という

条件分岐でも十分です。(笑)

 

 

(2-6)計算した値をリストに書き込む

 SharePointの「項目の作成」を使って、

 今まで計算してきた各種値を書き込んでいきます。

 

このフローはここで終了です。

 

 

(3)Teamsに通知するフロー

それでは、Teamsに通知するフローを作っていきます。

 

(3-1)トリガー:SharePointに項目が作成されたとき

 動いてほしいタイミングは「RotationListに項目が作成されたとき」なので

 そのようなフローを作成します。

 

(3-2)Teamsのチャネルにメッセージを投稿する

 作成されたリスト項目から各種値を取ってきて、

 Teamsのチャネル投稿文を作成します。

 

これですべての設計が完了です!

このように通知されます。

 

 

さいごに

はじめてのPowerPlatform関連の記事をシリーズもので書くという暴挙に出ました。

ブログを書く力が不足している中での記事だったので、読みにくい部分などがあったかと思います。

次回以降は、小ネタの投稿で練習したいと思います(笑)

 

【PowerAutomate】週替わりの当番を自動でTeamsに通知する(2-1)~(2-4)フロー作成編

※2022年8月現在の情報です。

 

yukio-ms365.hatenablog.com

こちらの記事の続きになります。

Power Automateでのフロー作成に入っていきます。

 

(2-1)作成開始、そしてトリガーの設定

今回は毎週決まった時間に動いてほしいフローなので・・・

(2-1-1)Power Automateのフローは

 「スケジュール済みクラウドフロー」を作成します。

 

 

(2-1-2)名前を付けて、

 「繰り返し間隔」は「1週」

 「設定曜日」を「金曜日」にして「作成」

 

 

(2-1-3)トリガーの部分を整えていきます。

 「タイムゾーン」は「(UTC+09:00) 大阪、札幌、東京」

 「開始時刻」の末尾に「Z」があるので、念のため取り除いておきます。

 ※この理由を説明すると長くなるので、後日別記事にします。

  「タイムゾーン」が関係してきます。(docsはこちら)

 「設定曜日」は金曜日のまま

 「設定時刻」を、フローが動いてほしい時刻に設定しておきます(ここでは8:30)



 

 

(2-2)RotationListから前回の当番情報を取ってくる

「SharePoint」の「複数の項目の取得」を使って、

前回の当番情報を取得してきます。

※初回の場合は、RotationListに手動で前回の当番を入れておく必要があります。

 

(2-2-1)「サイトのアドレス」と「リスト名」は、前回作ったRotationList

 「並べ替え順」に「Date desc」を指定することで

 「日付の降順(新しい順)」に並べ替えることができます。

 そうした上で「上から順に取得」に「1」を指定することで、

 前回分だけを取得することができます。

 ※もっと美しい取得方法があるのかも?とも感じています。

 

 

(2-3)変数の初期化

続いて、次週の当番計算や、RotationListへの情報格納に使う

各種変数を用意していきます。

 

(2-3-1)日付情報を格納する変数を初期化します。

 名前は「date_Monday」にしました。



「値」の欄に入れた関数はこちら

 convertTimeZone(addDays(utcNow(),3), 'UTC', 'Tokyo Standard Time’)

 金曜日に対して、月曜日の値を保存したかったので

 addDays関数で3日後を計算し、

 convertTimeZone関数で日本時間に変換して入れています。

 

 

(2-3-2)次は「来週の当番が誰か?」のIDを保存する変数です。

ここでは単純に名前をつけて、仮の値を入れておくのみです。

後で計算で使います。

※今回は3人ローテなので、同様の変数を3個(名前「t1」「t2」「t3」)

 そして、計算をするときの一次情報を保存する変数「tmp」を初期化しました。

 

 

(2-4)マスターリストの件数を変数に保存

今回、今週の当番から来週の当番を計算するにあたり

「割り算の余り」を使っていきます(詳しいことは後程)。

そのため、「マスターリストに何件保存しているのか?」という値、

要するに「何人でローテしているか?」の情報が必要でした。

その処理をしていきます。

 

(2-4-1)SharePointの「複数の項目の取得」を使って

 マスターリストから全件取得してきます。



 

(2-4-2)これでも全然いいんですが・・・

 このままでフローを保存するとこんな警告が出ます。

読んでみると

「全件取ってこいって言ってるけど、リストの件数大丈夫?」

みたいなことかな?と解釈しました。

確かに、全件取ってくるとなると、対象のリスト件数が膨大だと大変ですよね。

このことを、所属しているコミュニティで質問してみたら、

「フィルタークエリを何かしら入れたら警告消えると思うよ」

とのアドバイスを頂きました。

そこで・・・

 

 

(2-4-3)このようなフィルタークエリを入れてみましたところ、

 無事に警告を消すことに成功しました。

IDには1以上の値しかありませんので、

このように指定しても、全件取ってくることに変わりはありません。

【注意】

今回の操作は、対象のリスト「ToubanSample」が、

9行程度の少ない件数だからできたことです。

件数の膨大なリストから全件取ってくると、どこかで不具合が起きてくるはずです。

何件以上で不具合が起きるのか?はわからないので、

後日検証してみようと思います。

 

 

(2-4-4)先ほどの(2-4-3)で取ってきたリストを使って、

 ローテ総人数を計算して変数に保存していきます。

 

入れた関数はこちら

length(outputs('複数の項目の取得 ローテ総人数取得用')?['body/value'])

「式」タブで「length()」と入力し、「動的なコンテンツ」タブに切り替えて、

()の中にさっき取ってきたアイテム一覧を入れる感じです。

こちらも、コミュニティで教えていただきました。

ほんと、助けていただける仲間がいるって素晴らしいです。

 

中途半端ですが、今日はここまで。

次回↓↓↓、来週の当番を計算します。

yukio-ms365.hatenablog.com