サポート
補足情報
前半では紙面のプロンプト・ソースコードを抜粋し、後半ではPower Platformの試用版ライセンスについて記載します。
本書で掲載しているプロンプトとソースコード
本書Chapter 3,Chapter 4,Chapter 5,Chapter 7,Chapter 9の,実践編で掲載しているプロンプトおよびソースコードを公開します。
Chapter 3
P.62
「勤怠管理システム」を作成したいので,日別の勤怠テーブルと月ごとの勤怠サマリーを管理するテーブルを作成。
P.63
・「出退勤情報」テーブルを作成。スキーマ名は「shuttaikin_joho」。プライマリ列は1行テキスト型の「勤務日」。
・カラムは以下の2つを作成
出勤時刻(スキーマ名:shukkin_jikoku) / 日付と時刻
退勤時刻(スキーマ名:taikin_jikoku) / 日付と時刻
・既存の標準テーブルと「出退勤情報」テーブルの間に1対多のリレーションを作成。
取引先担当者
ユーザー
P.66
テーブルを作成。プライマリ列以外の列はのちほど追加するので作成しない。
テーブル:shuttaikin_joho
プライマリ列(1行テキスト):表示名: kinmubi
P.67
「shuttaikin_joho」テーブルに列を追加
shukkin_jikoku / 日付と時刻
taikin_jikoku / 日付と時刻
P.68
「shuttaikin_joho」テーブルの列の表示名を変更 kinmubi/勤務日
「shuttaikin_joho」テーブルの列の表示名を変更 shukkin_jikoku/出勤時刻
「shuttaikin_joho」テーブルの列の表示名を変更 taikin_jikoku/退勤時刻
「shuttaikin_joho」テーブルの表示名を「出退勤情報」に変更
P.71
「出退勤情報」テーブルに1対多のリレーションシップを作成。
・参照先: contact(既存の標準テーブル)
・参照先: systemuser(既存の標準テーブル)
P.76
テーブルを作成。プライマリ列以外の列はのちほど追加するので作成しない。
テーブル:shuttaikin_joho_summary
プライマリ列(1行テキスト):表示名: kinmuzuki
P.77
「shuttaikin_joho_summary」テーブルに列を追加
kinmu_nissu/ 整数
rodo_jikan / 1行テキスト
rodo_jikan_min / 期間
shonin_jokyo / 選択肢
P.77
「shuttaikin_joho_summary」テーブルの列の表示名を変更 kinmuzuki/勤務月
「shuttaikin_joho_summary」テーブルの列の表示名を変更 kinmu_nissu /勤務日数
「shuttaikin_joho_summary」テーブルの列の表示名を変更 rodo_jikan /労働時間
「shuttaikin_joho_summary」テーブルの列の表示名を変更 rodo_jikan_min / 労働時間(分)
「shuttaikin_joho_summary」テーブルの列の表示名を変更 shonin_jokyo/ 承認状況
「shuttaikin_joho_summary」テーブルの表示名を「出退勤情報サマリー」に変更
P.78
「出退勤情報サマリー」テーブルに1対多のリレーションシップを作成。
・参照先: contact (既存の標準テーブル)
・参照先: systemuser (既存の標準テーブル)
P.79
「出退勤情報サマリー」テーブルの「承認状況」列の選択肢のラベルと値を以下の内容に置き換え
未承認,0
承認待ち,11
承認済,21
差戻,91
P.80
「出退勤情報」テーブルと「出退勤情報サマリー」テーブルの間に多対1のリレーションを作成。
「出退勤情報」テーブルに検索列が追加されるようにする。
・表示名: 出退勤情報サマリー / 参照先: 「出退勤情報サマリー」テーブル
P.81
「出退勤情報」テーブルの列の表示名を変更 出退勤情報サマリー → 出勤月
P.83
DateDiff(出勤時刻, 退勤時刻, TimeUnit.Minutes) - If(DateDiff(出勤時刻, 退勤時刻, TimeUnit.Minutes) > 300, 60, 0)
P.86
Text(RoundDown('労働時間(分)'/60,0),"0") & ":" & Text(Mod('労働時間(分)',60), "00")
Chapter 4
P.108
・スケジュールトリガーで日本時間の毎週月曜日の10時にフローを開始
・Dataverseの「招待状(adx_invitations)」テーブルから複数件のレコードを抽出
抽出条件:statuscode(ステータス) = 1(新規)
・抽出した招待状テーブルのレコードを1件ずつループ処理
1.「取引先担当者(contacts)」テーブルをループ処理中のレコードの_adx_invitecontact_value(取引先担当者の招待(値))に設定されたIDで1件抽出
2.抽出した取引先担当者のメールアドレス宛にループ処理中のレコードの招待コード(adx_invitationcode)を付けて重要度はNormalでメールを送信
3.ループ処理中のレコードのadx_invitationidをキーに,招待状テーブルの該当レコードのstatuscode(ステータス)を756150000(送信済み)に更新
P.123
・関数で変数を使用する際は必ず variables('currentDate') のように関数形式で記述し,variables_currentDateという形式は絶対に使用しないでください。
・指定した変数以外は使用せず,関数での変換はまとめるようにしてください。
・Recurrence (月次),開始日時=2026-12-01T01:00:00Z,日本時間
・"currentDate"という名前の変数を作成します。addToTime関数で本日の2ヵ月後の日付を出して"yyyy-MM-01"形式に変換した値を設定します。
・"currentMonth"という名前の変数を作成します。currentDateの"MM"の部分を設定します。
・"targetMonth"という名前の変数を作成します。currentDateの"MM"の部分を設定します。
・Dataverseの「systemusers」テーブルから複数件のレコードを抽出。
抽出条件:ppc_kintai_kanri_taishosha_flag(勤怠管理対象者フラグ) = true(対象)
この先はForeachとDo Untilの二重のループ処理になります。
・抽出したユーザーテーブルのレコードを1件ずつループ処理する。
1.「ppc_shuttaikin_joho_summaries」テーブルにレコードを追加。アクションのParametersはentityNameのみとする。
2.Do Untilで以下の処理を繰り返し。
①「ppc_shuttaikin_johos」テーブルにレコードを追加。アクションのParametersはentityNameのみとする。
②addToTime関数でcurrentDateの翌日をyyyy-MM-dd形式で取得してcurrentDateに設定。値の設定はComposeアクションを経由する。
③作成した値をcurrentDateに設定。
④currentMonthにcurrentDateのMMの部分を設定します。
⑤Do untilのループ停止条件は以下のとおり。
「currentMonth」!=「targetMonth」の結果がtrueの場合。
3.Do Untilの終了後,currentDateに2ヵ月後の初日(yyyy-MM-01)を設定します。
P.123~124
関数で変数を使用する際は必ず variables('currentDate') のように関数形式で記述し,variables_currentDateという形式は絶対に使用しないでください。
・指定した変数以外は使用せず,関数での変換はまとめるようにしてください。
・Recurrence (月次),開始日時=2026-12-01T01:00:00Z,日本時間
・"currentDate"という名前の変数を作成します。addToTime関数で本日の2ヵ月後の日付を出して"yyyy-MM-01"形式に変換した値を設定します。
・"currentMonth"という名前の変数を作成します。currentDateの"MM"の部分を設定します。
・"targetMonth"という名前の変数を作成します。currentDateの"MM"の部分を設定します。
・Dataverseの「contacts」テーブルから複数件のレコードを抽出。
この先はForeachとDo Untilの二重のループ処理になります。
・抽出した取引先担当者テーブルのレコードを1件ずつループ処理する。
1.「ppc_shuttaikin_joho_summaries」テーブルにレコードを追加。アクションのParametersはentityNameのみとする。
2.Do Untilで以下の処理を繰り返し。
①「ppc_shuttaikin_johos」テーブルにレコードを追加。アクションのParametersはentityNameのみとする。
②addToTime関数でcurrentDateの翌日をyyyy-MM-dd形式で取得してcurrentDateに設定。値の設定はComposeアクションを経由する。
③作成した値をcurrentDateに設定。
④currentMonthにcurrentDateのMMの部分を設定します。
⑤Do untilのループ停止条件は以下のとおり。
「currentMonth」!=「targetMonth」の結果がtrueの場合。
3.Do Untilの終了後,currentDateに2ヵ月後の初日(yyyy-MM-01)を設定します。
P.130
yyyy-MM-dd形式で設定されているcurrentDateをyyyy/MM形式に変換
P.130
@formatDateTime(variables('currentDate'),'yyyy/MM')
P.135
currentDateに時刻(00:00)を付けてDataverseの日付型の項目に設定できるように整形
P.135
@concat(variables('currentDate'), 'T00:00:00Z')
P.142~143
指定したスケジュールで実行するクラウドフローです。
・毎週月曜の10時に起動,開始日時=2026-12-01T01:00:00Z,日本時間
・変数を作成します。(type:boolean)
・Dataverseのユーザー(systemusers)テーブルから複数件のレコードを抽出。
抽出条件:ppc_kintai_kanri_taishosha_flag(勤怠管理対象者フラグ) = true(対象)
・抽出したユーザーテーブルのレコードを1件ずつループします。ループの中でNo.1~No.4の処理を順番に実施します。
No.1 出退勤情報(ppc_shuttaikin_johos)テーブルから複数件のレコードを抽出。
抽出条件:当月の月初日 <= ppc_kinmubi < 本日
No.2 抽出した出退勤情報テーブルのレコードを1件ずつループ処理
(formatDateTime関数を使います。ppc_shukkin_jikoku(出勤時刻)とppc_taikin_jikoku(退勤時刻)の時刻を抽出して,どちらかが"00:00"の場合に変数にtrueを設定。)
No.3 変数の値がtrueか判定。(判定がTrueの場合:ループ中のユーザーへメール送信(Outlook))
No.4 変数にfalseを設定します。
P.143
指定したスケジュールで実行するクラウドフローです。
・毎週月曜の10時に起動,開始日時=2026-12-01T01:00:00Z,日本時間
・変数を作成します。(type:boolean)
・Dataverseの取引先担当者(contacts)テーブルから複数件のレコードを抽出。
・抽出した取引先担当者テーブルのレコードを1件ずつループします。ループの中でNo.1~No.4の処理を順番に実施します。
No.1 出退勤情報(ppc_shuttaikin_johos)テーブルから複数件のレコードを抽出。
抽出条件:当月の月初日 <= ppc_kinmubi < 本日
No.2 抽出した出退勤情報テーブルのレコードを1件ずつループ処理
(formatDateTime関数を使います。ppc_shukkin_jikoku(出勤時刻)とppc_taikin_jikoku(退勤時刻)の時刻を判定します。どちらかが"00:00"であれば変数にtrueを設定。)
No.3 変数の値がtrueか判定。(判定がTrueの場合:ループ中の取引先担当者へメール送信(Outlook))
No.4 変数にfalseを設定します。
P.146
本日の日付部分(dd)を取得して15以上か比較する数式を教えて
P.146
@greaterOrEquals(int(formatDateTime(utcNow(), 'dd')), 15)
P.147
月初日:formatDateTime(startOfMonth(utcNow()), 'yyyy-MM-dd')
本日:formatDateTime(utcNow(), 'yyyy-MM-dd')
P.148
出勤時刻:formatDateTime(items('Foreach_2')?['ppc_shukkin_jikoku'], 'HH:mm')
退勤時刻:formatDateTime(items('Foreach_2')?['ppc_taikin_jikoku'], 'HH:mm')
P.156
・関数で変数を使用する際は必ず variables('rodo_jikan_gokei') のように関数形式で記述し,variables_rodo_jikan_gokeiという形式は絶対に使用しないでください。
・複数の関数を1つの数式内で使用して,指定した変数以外は使用しないようしてください。
・変数名は指定された単語のみを使ってローマ字表記で統一してください。
・Foreachの値を参照する際は「items('Foreach')?[ʻカラム名ʼ]」形式にしてください。
・Dataverseのppc_shuttaikin_joho(出退勤情報)テーブルのppc_shukkin_jikoku (出勤時刻)またはppc_taikin_jikoku (退勤時刻)の更新をトリガーに起動します。
・integer型の変数を作成します。
変数1:rodo_jikan_gokei
変数2:kinmu_nissu_gokei
・複数件のレコードを以下の条件で抽出。
entityName:ppc_shuttaikin_johos
条件:_ppc_shuttaikin_joho_summaryid_value = トリガー出力の"_ppc_shuttaikin_joho_summaryid_value"
・抽出した出退勤情報テーブルのレコードを1件ずつループします。ループの中でNo.1の処理を実施します。
No.1:formatDateTime関数を使います。ループ中のレコードのppc_shukkin_jikokuとppc_taikin_jikokuの時間部分("HH:mm")がどちらも"00:00"以外の値か判定します。Trueの場合のみNo.2とNo.3の処理を行います。
No.2:「Compose」アクションで変数"rodo_jikan_gokei"にループ中のレコードの「ppc_rodo_jikan_min」を加算します。変数"rodo_jikan_gokei"に「Compose」アクションの出力を設定します。
No.3:「IncrementVariable」アクションを使って変数"kinmu_nissu_gokei"に1を加算します。
・ループ処理の終了後にppc_shuttaikin_joho_summariesを更新。
行ID=トリガー出力の_ppc_shuttaikin_joho_summaryid_value
P.159
出勤時刻:formatDateTime(items('Foreach')?['ppc_shukkin_jikoku'],'HH:mm')
退勤時刻:formatDateTime(items('Foreach')?['ppc_taikin_jikoku'],'HH:mm')
P.162
rodo_jikan_gokeiを60で割った値を"HH"として,rodo_jikan_gokeiを60で割った余りを"mm"として"HH:mm"形式の値に変換。HHとmmは前ゼロ埋めにする。
P.163
concat(formatNumber(div(variables('rodo_jikan_gokei'), 60), '00'), ':', formatNumber(mod(variables('rodo_jikan_gokei'), 60), '00'))
P.167
モデル駆動型アプリからキックするクラウドフローを作成したいです。どのトリガーでフローを作成すれば良いですか?
Chapter 5
P.224
SortByColumns(Search([@出退勤情報],SearchInput1.Text,ppc_kinmubi,ppc_kinmubi,ppc_rodo_jikan),"ppc_kinmubi",SortOrder.Ascending)
P.225
キャンバスアプリで,ギャラリーのitemsに以下のコードが書かれている。出退勤情報テーブルは標準テーブルのユーザーテーブルと多対一のリレーションシップがあり,その検索列は「ユーザー」という表示名で「ppc_systemuserid」という論理名。さらに,表示名が「勤務日」,論理名が「ppc_kinmubi」というテキスト型(yyyy-mm-dd)の列もある。ギャラリーに表示されるレコードがアプリを開いたユーザーのレコードのみ,勤務日が今月のレコードのみになるようにコードを修正して。
SortByColumns( Search( [@出退勤情報], SearchInput1.Text, ppc_kinmubi, ppc_kinmubi, ppc_rodo_jikan ), "ppc_kinmubi", SortOrder.Ascending )
P.225
SortByColumns(
Search(
Filter(
[@出退勤情報],
'ユーザー'.'User Name' = User().Email && StartsWith(ppc_kinmubi, Text(Today(), "yyyy-mm"))
),
SearchInput1.Text,
ppc_kinmubi,
ppc_kinmubi,
ppc_rodo_jikan
),
"ppc_kinmubi",
SortOrder.Ascending
)
P.226
提案してくれたコードで,Filter関数の「'ユーザー'.'User Name' = User().Email」と書いているところでエラーが出た。「'User Name'」という名前が認識されないみたいなんだけど,どう修正すればよい?
P.227
提案してくれたコードの「'ユーザー'.systemuserid = User().EntraObjectId」だとDataverseのGUIDとEntra IDの比較で違う要素の比較になっているよ。画像のどの要素を選べばよい?
P.228
SortByColumns(
Search(
Filter(
[@出退勤情報],
'ユーザー'.'Azure AD オブジェクト ID' = User().EntraObjectId && StartsWith(ppc_kinmubi, Text(Today(), "yyyy-mm"))
),
SearchInput1.Text,
ppc_kinmubi,
ppc_kinmubi,
ppc_rodo_jikan
),
"ppc_kinmubi",
SortOrder.Ascending
)
P.229
別画面で「出退勤情報サマリー」テーブルのギャラリーがあるんだけど,「出勤情報サマリー」の画面から遷移してきた場合だけ,選択された出退勤情報サマリーに紐づく出退勤情報のみ表示するようにコードを修正して。出退勤情報テーブルには,出退勤情報サマリーとリレーションシップを持つ検索列があり,その検索列の表示名は「出勤月」で論理名は「ppc_shuttaikin_joho_summaryid」。
P.231
Navigate('出退勤情報 画面',ScreenTransition.None, {SelectedSummary: ThisItem})
P.232
勤務日が今月のみという条件とサマリー画面から遷移した場合の条件が&&で結ばれているけど,本来は今月の出退勤情報を表示する場合と出退勤情報サマリー画面から遷移した場合を分けるべき。If文を使わずにコードを修正して。
P.232~P.233
SortByColumns(
Search(
Filter(
[@出退勤情報],
// アプリを開いたユーザーのみ
'ユーザー'.'Azure AD オブジェクト ID' = User().EntraObjectId &&
(
// ① 通常(今月のみ表示)
(IsBlank(SelectedSummary) && StartsWith(ppc_kinmubi, Text(Today(), "yyyy-mm"))
)
||
// ② サマリーから遷移した場合
(!IsBlank(SelectedSummary) && '出勤月'.出退勤情報サマリー = SelectedSummary.出退勤情報サマリー
)
)
),
SearchInput1.Text,
ppc_kinmubi,
ppc_rodo_jikan
),
"ppc_kinmubi",
SortOrder.Ascending
)
P.239
画像のようなフォームで表示するレコードを設定したい。フォームのitemプロパティに設定するコードを生成して。前提として,ギャラリー(RecordsGallery1)で表示している出退勤情報テーブルには勤務日(表示名が「勤務日」,論理名が「ppc_kinmubi」)というテキストの列があり,yyy-mm-dd形式で日付が記録されている。また,出退勤情報テーブルは出退勤情報サマリーテーブルと多対一のリレーションを持つ検索列(表示名は「出勤月」,論理名は「ppc_shuttaikin_joho_summaryid」)がある。また,ギャラリーのOnSelectプロパティにはあらかじめ「UpdateContext({ itemSelected: true, CurrentItem: ThisItem })」と設定されており,フォームで表示するレコードは出退勤情報画面の3種類の状況に合わせて変えたい。
1つ目はギャラリーで日付が選択されている場合で,この場合は選択された日付をフォームに表示。
2つ目はギャラリーで日付が選択されておらずギャラリーに現在の月が表示されている場合で,この場合は現在の日付をフォームに表示。
3つ目はギャラリーで日付が選択されておらずギャラリーに現在の月以外が表示されている場合で,この場合はその月の最初の日付をフォームに表示。
P.240
If(
// 1. ギャラリーで日付が選択されている場合
itemSelected,
CurrentItem,
// 2.,3.ギャラリーで日付未選択の場合
With(
{
GalleryMonthDate: DateValue(
First(RecordsGallery1.AllItems).ppc_kinmubi
)
},
If(
// 2. ギャラリーが現在の月の場合
Year(GalleryMonthDate) = Year(Today()) && Month(GalleryMonthDate) = Month(Today()),
// 今日の日付のレコード
LookUp(
RecordsGallery1.AllItems,
DateValue(ppc_kinmubi) = Today()
),
// 3. 現在の月以外→ その月の1日のレコード
LookUp(
RecordsGallery1.AllItems,
DateValue(ppc_kinmubi) =
Date(
Year(GalleryMonthDate),
Month(GalleryMonthDate),
1
)
)
)
)
)
P.242
画像のような画面で,出勤ボタンを押したときは出勤時間に現在時刻を記録,退勤ボタンを押したときは退勤時刻に現在時刻を記録したい。ボタンのOnSelectとそれぞれの時刻のDefaultに設定するコードを生成して。それぞれの時刻のDefaultにはあらかじめParent.Defaultのようにテーブルのデータが表示されるよう設定されている。また,ギャラリーでレコードを切り替えた時にボタンを押した時刻が残らないようにしたい。
P.243
・出勤ボタン
Set(
varStartTime,
Now()
);
・出勤日付
If(
IsBlank(varStartTime),
Parent.Default,
DateValue(varStartTime)
)
・出勤時刻(時間)
If(
IsBlank(varStartTime),
Text(Hour(Parent.Default),"00"),
Text(Hour(varStartTime), "00")
)
・出勤時刻(分)
If(
IsBlank(varStartTime),
Text(Minute(Parent.Default),"00"),
Text(Minute(varStartTime), "00")
)
・退勤ボタン
Set(
varEndTime,
Now()
);
・退勤日付
If(
IsBlank(varEndTime),
Parent.Default,
DateValue(varEndTime)
)
・退勤時刻(時間)
If(
IsBlank(varEndTime),
Text(Hour(Parent.Default),"00"),
Text(Hour(varEndTime), "00")
)
・退勤時刻(分)
If(
IsBlank(varEndTime),
Text(Minute(Parent.Default),"00"),
Text(Minute(varEndTime), "00")
)
P.244
Set(varStartTime, Blank());
Set(varEndTime, Blank());
UpdateContext({itemSelected: false, CurrentItem: Blank()});
Set(varStartTime, Blank());
Set(varEndTime, Blank());
P.246
「出退勤情報」テーブルには「労働時間(分)」(論理名は「ppc_rodo_jikan_min」)という列があり,Dataverseの機能「出勤時刻」(日付と時刻の列)と「退勤時刻」(日付と時刻の列)から1日の労働時間が分の単位で自動計算され整数で保存されている。また,「出退勤情報サマリー」テーブルと多対一のリレーションシップを持つ検索列(表示名「出勤月」,論理名「ppc_shuttaikin_joho_summaryid」)がある。
キャンバスアプリの出退勤情報のフォーム(Form1)の保存ボタンを押したときに以下の処理をしたい。ボタンのOnSelectに設定するコードを生成して。
1.フォームで保存した「出退勤情報」レコードの「出勤月」(「出退勤情報サマリー」レコード)が持つ「出退勤情報」レコードをすべて取得し,それらの労働時間(分)を合計する。
2.1.で取得した「出退勤情報レコード」で労働時間(分)が0より大きいレコードをカウントする。
3.1.で計算した値をフォームで保存した「出退勤情報」レコードに紐づく「出退勤情報サマリー」レコードの「労働時間(分)」(論理名「ppc_rodo_jikan_min」)という列に整数で保存する。
4.1.で計算した値をhh:MM形式に変換して「出退勤情報サマリー」レコードの「労働時間」(論理名「ppc_rodo_jikan」)という列にテキストで保存する。
5.2.で計算した値を「出退勤情報サマリー」レコードの「勤務日数」(論理名「ppc_kinmu_nissu」)という列に整数で保存する。
P.246~P.247
SubmitForm(Form1);
// ① 保存後のレコードにひもづく「出勤月(サマリー)」を変数に保持
Set(
varSummary,
Form1.LastSubmit.ppc_shuttaikin_joho_summaryid
);
// ② 同じ出勤月を持つ出退勤情報レコードをすべて取得
ClearCollect(
colShuttaikin,
Filter(
出退勤情報,
ppc_shuttaikin_joho_summaryid.出退勤情報サマリー = varSummary.出退勤情報サマリー
)
);
// ③ 労働時間(分)の合計を計算
Set(
varTotalMinutes,
Sum(
colShuttaikin,
ppc_rodo_jikan_min
)
);
// ④ 労働時間(分)が0より大きいレコード数(勤務日数)を計算
Set(
varWorkDays,
CountRows(
Filter(
colShuttaikin,
ppc_rodo_jikan_min > 0
)
)
);
// ⑤ 分→ hh:MM 形式に変換
Set(
varHoursText,
Text(
Int(varTotalMinutes / 60),
"[$-ja-JP]00"
) & ":" &
Text(
Mod(varTotalMinutes, 60),
"[$-ja-JP]00"
)
);
// ⑥ 出退勤情報サマリーを更新
Patch(
出退勤情報サマリー,
varSummary,
{
// 3. 労働時間(分)
ppc_rodo_jikan_min: varTotalMinutes,
// 4. 労働時間(hh:MM)
ppc_rodo_jikan: varHoursText,
// 5. 勤務日数
ppc_kinmu_nissu: varWorkDays
}
);
P.249
P.251
キャンバスアプリのギャラリーでitemsに設定している以下のコードを修正して。出退勤情報サマリーテーブルは標準テーブルのユーザーテーブルと多対一のリレーションシップがあり,その検索列は「ユーザー」という表示名で「ppc_systemuserid」という論理名。さらに,表示名が「勤務月」,論理名が「ppc_kinmuzuki」というテキスト型(yyyy/mm)の列もある。ギャラリーに表示されるレコードがアプリを開いたユーザーのレコードのみ,勤務月の昇順に並ぶようにしたい。
Search([@出退勤情報サマリー], SearchInput2.Text, ppc_kinmuzuki, ppc_kinmuzuki,ppc_rodo_jikan)
P.252
SortByColumns(
Search(
Filter(
[@出退勤情報サマリー],
'ユーザー'.'Azure AD オブジェクト ID' = User().EntraObjectId
),
SearchInput2.Text,
ppc_kinmuzuki,
ppc_rodo_jikan
),
"ppc_kinmuzuki",
SortOrder.Ascending
)
P.254
P.255
画像のようなフォームで,承認状況(DataCardValue9)が「未承認」のときだけ承認申請ボタンを押下できるようにしたい。DataCardValue9のitemsには「Choices('承認状況 (出退勤情報サマリー)')」と設定されている。承認申請ボタンのDisplayModeプロパティに設定するコードを生成して。
If(
DataCardValue9.Selected.Value = '承認状況 (出退勤情報サマリー)'.未承認,
DisplayMode.Edit,
DisplayMode.Disabled
)
P.256
画像のような「出退勤情報サマリー」テーブルのレコードを表示するフォーム(Form2)があり,フォームのitemには「RecordsGallery2.Selected」と設定されている。承認申請「承認状況」列は,選択肢列で「未承認」「承認待ち」「承認済」「差戻」の4つの選択肢がある。「承認状況」のDataCard名は「DataCard9」。承認申請ボタンを押したときにフォームで表示しているレコードの「承認状況」列の値を「承認待ち」に更新してフォームの表示も更新したい。ボタンのOnSelectに設定するコードを生成して。
Patch(
'出退勤情報サマリー',
RecordsGallery2.Selected,
{
承認状況: '承認状況 (出退勤情報サマリー)'.承認待ち
}
);
Refresh('出退勤情報サマリー');
ResetForm(Form2);
Chapter 7
P.332
今日の出勤時刻・退勤時刻をフォームから入力し,勤怠データとして登録するページの作成
入力項目
勤務日:本日の日付(自動設定・編集不可)
出勤時刻:時刻を入力
退勤時刻:時刻を入力
保存先(Dataverse:出退勤情報)
勤務日:ppc_kinmubi
出勤時刻:ppc_shukkin_jikoku
退勤時刻:ppc_taikin_jikoku
フォーム送信後,入力内容がDataverseの「出退勤情報」テーブルに登録
P.335
出退勤情報を月次で一覧表示するWebページを作成する
選択した月の出退勤情報を日別に表示し,勤怠の確認・修正・申請を行う画面を作成する
P.349
Power Pagesで使用するJavaScriptコードを作成してください。
要件:
・DOMContentLoaded後に処理を実行する
・id が「syuttaikinn-button-area」の要素を取得し,中央寄せレイアウトを適用する
・現在時刻(日本時間,時:分:秒)をリアルタイムで表示する時計を表示する
・「出勤」「退勤」の2つの大きなボタンを横並びで表示する
・出勤ボタン押下時は「出退勤フラグ = 0」
・退勤ボタン押下時は「出退勤フラグ = 1」
・Microsoft公式を参照し,shell.ajaxSafePostを使用する
・処理中はボタンを無効化し,「処理中…」と表示する
・成功時は「勤怠を入力しました。」とalert表示する
・失敗時はエラーメッセージをalert表示する
制約:
・HTMLは生成せず,JavaScriptのみを出力する
・外部ライブラリは使用しない
・コメントを多めに入れ,教育用として読みやすくする
P.349~353
const CLOUD_FLOW_SYUTTAIKIN_URL = "クラウドフローのURL";
window.addEventListener('DOMContentLoaded', function () {
// idで一意に取得
const container = document.getElementById("syuttaikinnbutton-
area");
if (!container) return;
// 念のため中身をクリア
container.innerHTML = "";
/*ボタンを中央やや上に配置*/
container.style.display = "flex";
container.style.flexDirection = "column";
container.style.justifyContent = "center";
container.style.alignItems = "center";
container.style.height = "100%";
container.style.transform = "translateY(-25%)";
// === 時計表示 ===
const clock = document.createElement("div");
clock.id = "current-time";
/* 文字 */
clock.style.fontSize = "220%";
clock.style.fontWeight = "700";
clock.style.fontFamily = "monospace";
clock.style.letterSpacing = "0.12em";
/* レイアウト */
clock.style.marginBottom = "4%";
clock.style.padding = "5% 10%";
/* 見た目(やさしさ重視) */
clock.style.backgroundColor = "#f4f6f8"; // やや暖かいグレー
clock.style.color = "#333"; // 真っ黒にしない
clock.style.border = "0.12em solid #e0e3e7"; // 薄く・淡い枠
clock.style.borderRadius = "16%"; // しっかり丸める
/* ふんわり影 */
clock.style.boxShadow = "0 0.4em 1em rgba(0, 0, 0, 0.08)";
function updateTime() {
const now = new Date();
clock.textContent = now.toLocaleTimeString('ja-JP', {
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
}
updateTime();
setInterval(updateTime, 1000);
container.appendChild(clock);
// ボタン横並び用ラッパー
const buttonRow = document.createElement("div");
buttonRow.style.display = "flex";
buttonRow.style.gap = "24px";
buttonRow.style.width = "100%";
buttonRow.style.maxWidth = "420px";
// 出勤ボタン
const syukkinButton = document.createElement("button");
syukkinButton.id = "syukkin-button";
syukkinButton.type = "button";
syukkinButton.textContent = "出勤";
syukkinButton.className = "btn btn-success";
syukkinButton.style.flex = "1";
syukkinButton.style.padding = "36px 0";
syukkinButton.style.fontSize = "2.2rem";
syukkinButton.style.fontWeight = "800";
// 退勤ボタン
const taikinButton = document.createElement("button");
taikinButton.id = "taikin-button";
taikinButton.type = "button";
taikinButton.textContent = "退勤";
taikinButton.className = "btn btn-danger";
taikinButton.style.flex = "1";
taikinButton.style.padding = "36px 0";
taikinButton.style.fontSize = "2.2rem";
taikinButton.style.fontWeight = "800";
// DOM構築
buttonRow.appendChild(syukkinButton);
buttonRow.appendChild(taikinButton);
container.appendChild(buttonRow);
const syukkin_button = document.getElementById("syukkin-button");
const taikin_button = document.getElementById("taikin-button");
if (!syukkin_button || !taikin_button) {
console.warn("ボタンが取得できません");
return;
}
/*****************************************************
* 出勤
*****************************************************/
syukkin_button.onclick = function () {
syukkin_button.disabled = true;
taikin_button.disabled = true;
syukkin_button.textContent = "処理中…";
let no = '0';
setTimeout(() => {
try {
kick_cloud_flow_syuttaikin(no);
console.log("出勤しました");
} catch (error) {
console.log(error.message);
}
}, 1500);
setTimeout(() => {
syukkin_button.disabled = false;
taikin_button.disabled = false;
syukkin_button.textContent = "出勤";
taikin_button.textContent = "退勤"
}, 2000);
};
/*****************************************************
* 退勤
*****************************************************/
taikin_button.onclick = function () {
syukkin_button.disabled = true;
taikin_button.disabled = true;
taikin_button.textContent = "処理中…";
let no = '1';
console.log("退勤しました");
kick_cloud_flow_syuttaikin(no);
setTimeout(() => {
syukkin_button.disabled = false;
taikin_button.disabled = false;
syukkin_button.textContent = "出勤";
taikin_button.textContent = "退勤"
}, 2000);
};
});
function kick_cloud_flow_syuttaikin(no) {
console.log(JSON.stringify({ "出退勤フラグ": no }));
shell.ajaxSafePost({
type: "POST",
url: CLOUD_FLOW_SYUTTAIKIN_URL,
data: { "eventData": JSON.stringify({ "出退勤フラグ": no}) }
})
.done(function () {
alert('勤怠を入力しました。');
})
.fail(function () {
alert("フローの実行に失敗しました。" + "\n" + "再読み込みして状況を確認してください。" + "\n" + "何度もエラーが発生する場合は,管理者にお問い合わせください。");
});
}
P.353~354
<div class="col-md-6 columnBlockLayout" style="
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-width: 250px;
">
<div style="display: flex; gap: 24px; width: 100%; maxwidth: 420px;">
<button type="button" id="syukkin-button" class="btn btnsuccess" style="
flex: 1;
padding: 36px 0;
font-size: 2.2rem;
font-weight: 800;
letter-spacing: 0.15em;
">
出勤
</button>
<button type="button" id="taikin-button" class="btn btndanger" style="
flex: 1;
padding: 36px 0;
font-size: 2.2rem;
font-weight: 800;
letter-spacing: 0.15em;
">
退勤
</button>
</div>
</div>
P.354
<div id="syuttaikinn-button-area" class="col-md-6 columnBlockLayout"
P.377~378
Power PagesのBasic Formに対して動作するJavaScriptを作成してください。
目的:
・フォーム上で「承認待ちに変更」ボタンを追加し,クリック時にPower Automate(Cloud Flow)を実行する
要件:
・DOMContentLoaded後に処理を実行する
・フィールドのIDが「ppc_shonin_jokyo」のinput要素を取得する
・そのフィールドが属する.control要素を取得し,その中にボタンを追加する
・「承認待ちに変更」という文言のボタンを動的に生成する
・ボタンは<a>要素で作成し,classに「btn button1 shonin-btn」を指定する
・上部要素との間隔としてmargin-topを%指定で設定する
・ボタン押下時には確認ダイアログを表示する
動作:
・URLパラメータ「id」からフォームのGUIDを取得する
・GUIDをCloud Flowに渡してPOST実行する
・Cloud FlowのURLは
"/_api/cloudflow/v1.0/trigger/883a10a9-522b-f111-88b5-7ced8d30691a"
・shell.ajaxSafePostを使用してCloud Flowを呼び出す
・成功時は「承認待ちに変更しました。承認をお待ちください。」とalert表示
・失敗時はエラーメッセージをalert表示
その他条件:
- 既存のUpdateボタン(ID:UpdateButton)は非表示にする
- 関数は以下の名前を使用する
- getFormGuidFromUrl
- kick_cloud_flow_shoninmachi
- clickShoninmachiButton
- 教育用コードとして,処理の区切りごとにコメントを多めに入れる
- HTMLは書かず,JavaScriptのみを出力する
- 最終的な出力はJavaScriptのコードブロックのみとする
P.378~380
const CLOUD_FLOW_SHOUNIMACHI_URL = "ここにクラウドフローのURLを入力";
// Basic Form の読み込み完了後に実行
window.addEventListener('DOMContentLoaded', function () {
// 承認状況フィールドの control 要素を取得
const field = document.getElementById("ppc_shonin_jokyo");
const control = field.closest(".control");
// 承認待ちに変更ボタン生成
const btn = document.createElement("a");
btn.href = "javascript:void(0);";
btn.id = "shoninmachi-btn";
btn.className = "btn button1 shonin-btn";
btn.textContent = "承認待ちに変更";
// 上の要素との間隔を % で指定
btn.style.marginTop = "3%";
btn.style.display = "inline-block";
btn.onclick = clickShoninmachiButton;
// control の中に追加
control.appendChild(btn);
// Update ボタンを非表示
const updateBtn = document.getElementById("UpdateButton");
if (updateBtn) {
updateBtn.style.display = "none";
}
});
/*****************************************************
* 3. フォームのGUIDを取得する関数
*****************************************************/
function getFormGuidFromUrl() {
const params = new URLSearchParams(window.location.search);
const ids = params.get("id") || "";
return String(ids);
}
/*****************************************************
* 4. クラウドフローをキックするための関数
*****************************************************/
function kick_cloud_flow_shoninmachi(ids) {
console.log(JSON.stringify({ "guids": ids }));
shell.ajaxSafePost({
type: "POST",
url: CLOUD_FLOW_SHOUNIMACHI_URL,
data: { "eventData": JSON.stringify({ "guids": ids })
}
})
.done(function () {
alert('承認待ちに変更しました。承認をお待ちください。');
})
.fail(function () {
alert("フローの実行に失敗しました。" + "\n" + "再読み込みして状況を確認してください。" + "\n" + "何度もエラーが発生する場合は,管理者にお問い合わせください。");
});
}
/*****************************************************
* 5. 承認待ちに変更するクラウドフロー
*****************************************************/
this.clickShoninmachiButton = function () {
const ids = getFormGuidFromUrl();
setTimeout(() => {
if (window.confirm('表示されている月の勤務データを「承認待ち」にします。よろしいですか?')) {
setTimeout(() => {
try {
kick_cloud_flow_shoninmachi(ids);
} catch (error) {
console.log(error.message);
}
}, 1500);
};
}, 1500);
}
Chapter 8
P.392
Dataverseに蓄積したデータをPower BIで分析したいです。以下の2つのテーブル
のデータからどのような分析ができますか? レポートのページのイメージ画像も付
けて教えてください。
出退勤情報のデータからどのような分析ができると思いますか? 持っている項目は
以下の内容です。
①出退勤情報テーブル
・勤務日 …yyyy-MM-dd形式のテキスト
・出勤時刻…日付時刻
・退勤時刻…日付時刻
・労働時間…HH:mm形式のテキスト
・労働時間(分) …480などの数値
②出退勤情報サマリーテーブル(月次情報)
・勤務月 …yyyy/MM形式のテキスト
・勤務日数…出退勤の時刻が登録された日数
・労働時間…HH:mm形式のテキスト
・労働時間(分) …2480などの数値(1ヵ月分)
・承認状況…「未承認」「承認」
P.393
画像イメージのページを作成したいのですが,各ビジュアルのX軸やY軸といったプロパティにはどの項目を当てはめていけばいいですか?
Power Platform試用版ライセンスの取得方法
Power Platformを利用する環境がない方を対象とした,1ヵ月の試用版ライセンスで個人環境を作成する手順を紹介します。1ヵ月後に費用請求されないための手順も案内しますので,最後まで設定を行うようにお願いします。
Microsoft 365の試用版の申し込み
新しいテナント用にプロファイルを作成します。この先の手順はWindows 11とMicrosoft Edgeを使って手順を紹介します。Edge右上の「…」をクリックして「設定」を開きます。普段お使いのアカウントと区別をする必要がなければ,プロファイル作成は不要です。/

「プロファイルの追加」をクリックします。

メッセージが表示されたら「追加」をクリックします。

新しくEdgeのウィンドウが開きます。「ユーザーデータを使用せずに開始」をクリックします。

【1ポイントアドバイス】
Windows 11のツールバーに,追加したプロファイル用のEdgeのアイコンが表示されます。アイコンを右クリックして「タスクバーにピン留めする」をクリックしておくと次回からの利用時に便利です。

◆ ◆ ◆
「ようこそ」ページが開きます。以下のURLをアドレスバーに打ち込んでMicrosoft 365の申し込みページへ遷移します。
https://www.microsoft.com/ja-jp/microsoft-365/business

Microsoft 365のページが開きます。

下へスクロールすると製品説明のセクションがあります。BasicとStandard,Premiumの3種類がありますが,どれを選択しても大丈夫です。今回はPremiumの「無料で試す」を選択して進みます。

もしも以下の画面が出たら普段お使いのアカウントでサインインした状態のため,赤枠のボタンをクリックしてログアウトしてください。

サインアウトするアカウントを選択します。

試用版の申し込み画面では,念のため1ヵ月のサブスクリプション契約にしておきます。

次にメールアドレスを入力します。こちらにはGmailなどのフリーアドレスを入力してください。会社でお使いのMicrosoftアカウントなどを入力するとテナント作成ができないので気をつけてください。メールアドレスを入力したら「次へ」をクリックします。

「アカウントのセットアップ」をクリックします。

入力したメールアドレス宛に届いた確認コードを入力して「確認」ボタンをクリックします。

アンケートのページが開いたら各項目を入力します。*が付いた項目は入力必須です。

入力が終わったら「次へ」ボタンをクリックします。

ユーザー名とドメイン名,パスワードを入力します。ユーザー名とドメイン名の組み合わせがアカウントになりますので,手元に控えておきます。入力が終わったら「次へ」をクリックします。

アカウントを作成するために少々時間がかかります。次は支払い方法を追加します。1ヵ月間は無料,そのあとも契約を継続しなければ費用はかからないので安心してください。

入力後に「保存」ボタンをクリックします。クレジットカードの認証は案内に沿って対応してください。

画面が切り替わったら表示内容を確認します。

問題なければ「無料版を開始」ボタンをクリックします。少し時間がかかるので待ちます。

アカウントのセキュリティ設定
作成したアカウントの認証設定を行います。「次へ」ボタンをクリックしてアカウントの認証設定を実施します。

作成したアカウントで認証を通します。

Microsoft Authenticatorの設定を行います。

ご自身のスマートフォンにAuthenticatorがインストールされていれば「次へ」ボタンをクリックします。インストールしていなければアプリストアからインストールしてください。

「次へ」ボタンをクリックします。

スマートフォンのAuthenticatorでQRコードをスキャンします。

Authenticatorに通知が来たら認証します。

「完了」ボタンをクリックします。

再度サインインを行います。

Authenticatorで認証します。

「はい」ボタンをクリックします。

試用版の利用開始
画面下部のボタンをクリックして試用版の使用を開始します。

他のユーザーを追加する場合は「続ける」をクリック,現時点で追加不要であれば「セットアップの終了」をクリックします。

セットアップを終了する場合はセットアップ終了の利用を回答します。

ユーザーを追加する場合は氏名とユーザー名を入力して「ユーザーを追加して続行する」をクリックします。

「完了」をクリックしたら終了です。

セットアップの終了後はMicrosoft 365管理センターが開きます。セットアップでユーザーを追加した場合はこちらのページで確認できます。あとからユーザーを追加する場合は左側の「ユーザー」のメニューから実施してください。

試用版ライセンスの自動継続のキャンセル
続いて試用版ライセンスの自動継続の設定をオフします。オフにしても1ヵ月間は利用可能ですので安心してください。左側のメニューから「課金情報」→「お使いの製品」を選択し,試用版のライセンスをクリックします。

「継続請求を編集する」をクリックします。

「有効期限切れ時にキャンセルする」を選択して「保存」ボタンをクリックします。

「はい」ボタンをクリックします。

少し待つと右側のエリアが切り替わります。「完了」ボタンをクリックします。

継続請求の表示が切り替わったら設定完了です。試用が30日で完了しない場合は試用版の延長(30日間)が可能です。
