気が付けば、2025年になっている、、、。
しばらくサボっていたのでブログの書き方を忘れた、、、。いや、いったん忘れて生まれ変わったほうがいいかも(これまでの読んでそう思った、、、。)
気を取り直して、日付を自動計算して出力したくて、計算式(拡張)っていうのを使ってみたら震えるほど感動したので、ここに記します。
※おことわり
2025年1月時点の情報です。プリザンターのバージョンは 1.4.9.1 です。Google Chrome でやっています。
javascript,html,cssともに初心者です。調べながら、やってみながら、きっとこうすればいいんだ!という感じで書いていますので、間違っている、または効率的な書き方ではない可能性が大いにあります。間違ってるよ!とか、こうしたほうがいいよ!ということがありましたら、コメント等で教えていただけると大変ありがたいです。
1.はじめに
プリザンターの「リマインダー」機能で、リマインドを送信する、というのをやったら「期限前なのに督促するとは何事だ」と電話で怒られた。親切に「そろそろ期限ですよ」って思い出しメールなんだけど、とか、電話する暇があるならちゃっちゃとやったら??とか、思うところは多々ありましたがぐっとこらえて期限の日付の翌日に通知が行くように改造しようと。一日遅れのリマインド設定ができないっぽいので、別に日付項目を設けてその日付項目でリマインド設定をしようと。
で、日付の計算ができそうだったので、計算式(拡張)というのを使ってみた。
で、エクセル関数好きは感動で震えた。スクリプトはヨチヨチ歩きだけどエクセルならまかせろというエクセル関数好き同志の皆さん(いますかね?)に朗報!プリザンターの計算式(拡張)だと、エクセル感覚で計算式組めて楽しいかも!
今回はいったんリマインダーのことは忘れて、日付の加算減算をいくつかの方法でやってみました。計算式(拡張)一択でいいじゃん、と言いたいところですが、自分の会社ではバージョンの関係で計算式(拡張)が使えない、また、javaScriptも勉強中のため自主練もかねてスクリプト等でもいろいろやってみました。(なので、スクリプトの項番5,6は読み飛ばしていただいて大丈夫です(^^♪)
2.計算式(拡張)とは
「計算式」タブで設定します。
「計算方法」で「規定」か「拡張」かを切り替えします。
規定は数値項目に対して四則演算ができます。
拡張は[数値項目][分類項目][日付項目][説明項目][チェック項目]に対して、四則演算、専用の関数を使用して計算式が設定できます。
計算式(拡張)は規定に比べて実現できることの可能性が大いに広がりますね!
テーブルの管理:計算式
テーブルの管理:計算式(既定)
テーブルの管理:計算式(拡張)
3.参考:自動ポストバックとは
計算式をやっていると、ちょいちょい出てくるのが、自動ポストバック。計算式じゃなくても出てくるけど。なんとなーくの理解だったので、改めて調べてみました。
++++++++++++++++++++++++++++++++++++++++++
Web通信において、ブラウザ上で入力されたデータをサーバーに送信することをポストという。
(入力フォームに名前とか入力して「送信」ボタン押したりするやつ)
ポストバックとは、入力フォームからデータ送信 → サーバーで処理 → 自分自身のページが返ってくる、ということを指す。
POSTしてBACK。はがきに「1+1」って書いてポストに投函したら郵便局でそのはがきに「2」って書いて送り返してきた、みたいなイメージ?
プリザンターのポストバックではどんなことが起きているのかな、とイメージしてみると、
例えば、日付Aの入力値をもとに日付Bの値を算出する計算式を設定していた場合
①日付Aを入力する
→②クライアント側(ブラウザ)からサーバーに入力内容が送信される
→③サーバーで処理が行われる(今回のケースでは計算式が実行され、日付Bの値が算出される)
→④計算後の当該ページの内容がサーバーからクライアント側(ブラウザ)に送信される
→⑤送られてきたデータを再読み込みして、画面内容が更新される
②~⑤がポストバックの動き。
通常は「更新」(または「作成」)ボタンクリック時に②~⑤が行われる。
自動ポストバックをオンにした項目については、①の入力後直ちに自動でポストバック処理が走り②~⑤の処理が行われる。「自動」だから。
自動ポストバックではなく、「更新」ボタンクリックのタイミングで計算が行われる場合は③の時に計算と更新が行われ、DBのレコードのデータも書き換わる。対して自動ポストバックでの計算実行は計算は行われるが、更新処理は行われないのでレコードの内容を確定したいときは「更新」ボタンを別途押す必要がある。
自動ポストバックで返却する項目を指定した場合は、指定した項目のみ再読み込みが行われる。返却する項目を指定しない場合、自動ポストバック処理ではすべての項目が再読み込みされる。項目数が多い場合等は、返却する項目を指定することで、更新処理が軽くなる。
つまり返却項目を指定した場合は⑤のところが指定した項目のみのデータになるんでしょうね。たぶん。
参考:ポストバックってなあに?
[ASP.NET]ポストとポストバックの違いは?
テーブルの管理:エディタ:項目の詳細設定:自動ポストバック
テーブルの管理:エディタ:項目の詳細設定:自動ポストバック時に返却する項目
+++++++++++++++++++++++++++++++++
自動ポストバックをわかっていない時、計算結果のほうの項目を自動ポストバックオンにして「なにも変わらないんだけど。。。」と涙目になっていた恥ずかしい過去があります。
計算式でいうと引数となる項目のほうの自動ポストバックをオン!です。
4.計算式(拡張)を使用した日付の加算、減算
(1)やりたいこと
日付項目Aの日付が入力されたら、日付項目Bに●日後の日付が自動設定されるようにしたい。
また、日付項目Aの日付が入力されたら、日付項目Cに●日前の日付が自動設定されるようにしたい。
(2)エディタの設定
日付A:元となる日付を入力する項目。
- 表示名は「基準日」とし、規定値に「0」を設定(レコードを作成した日付が入ります)、書式は「年月日」としました。
- 自動ポストバックをオンにします。
- 返却する項目に日付B、日付Cを設定します(DateB,DateC)
※上記の設定は、やらなくても大丈夫です。
- 自動ポストバックをオンにすることで、日付Aを入力すると直ちに計算式が実行され日付B,日付Cに計算結果が反映されます。自動ポストバックをオンにしない場合は「作成」または「更新」ボタンをクリックしたときに計算式が実行されます。
- 返却する項目を設定しない場合でも、結果は同じです。
(3)日付の計算式
公式マニュアル 計算式(拡張)の関数一覧 を見てみます。
日付の計算には $DATE が使えそうですね。
$YEAR $MONTH $DAY を組み合わせて使うとよさそうです。愛しのexcelといっしょ(excelでは$はついてないけど)。
(4)日付の加算・減算
まず、日付Bに「基準日」(日付A)の7日後の日付を出力する計算式を設定していきます。
①「計算式」タブ→新規作成
②[計算方式] に「拡張」を指定
③[対象]に「日付B」を指定 ※計算結果を出力したい項目を設定します。
④計算式を入力。「基準日」の7日後の日付を出力したいので、以下の計算式とします。
$DATE($YEAR(基準日),$MONTH(基準日),$DAY(基準日) + 7)
次に、日付Cの計算式を設定します。
上記①~③の手順で④の計算式は「基準日」の7日前の日付を出力したいので + 7 の部分を - 7 に変更した計算式を入力します。
$DATE($YEAR(基準日),$MONTH(基準日),$DAY(基準日) – 7)
(5)結果
「新規作成」でレコードを作成してみます。
「基準日」には当日が規定値で入るようにしています(今日は 2025/1/5 )
「日付A」には 2025/1/12 「日付B」には 2024/1/29 が表示されています。
ちゃんと、年も月も繰り下がりもやってくれるなんて。。。感動してしまった。
基準日を手動で変更すると、すぐに「日付B」「日付C」の値も変わります。素敵。。。
(6)数値項目分だけ日付の加減算をする
ちょっと趣向を変えて、数値Aを設けて、数値Aに指定した日数だけ日付の加算・減算をするようにしてみます。
- エディタ:数値Aを有効化し、表示名を「加減算日数」とする。自動ポストバックをオンにする。
- 計算式:(4)の計算式を修正する。 +7, -7 の「7」の部分を数値Aの表示名「加減算日数」に変更する。
日付B:$DATE($YEAR(基準日),$MONTH(基準日),$DAY(基準日) – 加減算日数)
日付C:$DATE($YEAR(基準日),$MONTH(基準日),$DAY(基準日) – 加減算日数)
これだけで!「日付A」「加減算日数」を変更すると「日付B]「日付C」が変わります。
(7)$IFで条件分岐してみる
私の悪い癖。いろいろやってみたくなる。
分類項目を設けて「加算」「減算」を指定して、数値で指定した日数を加算/減算した結果を日付Bに表示する、というのをやってみた。
- エディタ
- 数値A:表示名を「日数」に変更
- 分類A:表示名「加算減算区分」、選択肢[100,加算/200,減算]、規定値[100]、自動ポストバックオン
- 計算式
- 「日付C」の計算式は削除
- 「日付B」の計算式を以下の計算式に変更
$IF( 加算減算区分 == ‘100’ , $DATE($YEAR(基準日),$MONTH(基準日),$DAY(基準日) + 日数), $DATE($YEAR(基準日),$MONTH(基準日),$DAY(基準日) – 日数))
または
$DATE($YEAR(基準日),$MONTH(基準日),$DAY(基準日) + $IF( 加算減算区分 == ‘100’ , 日数, 日数 * -1))
ステキ!
(8)表示名を使用しない場合の書き方
表示名の日本語の入力だるい、なんて時は項目名を [NumA] のようにブラケット(角カッコ)でくくって入力すると、勝手に表示名に書き換えてくれます。地味に便利。
また、「表示名を使用しない」にチェックを入れたときは項目名の指定が [DateA] のような指定の仕方になります。表示名だと「なんだっけ?」ってなったりする場合や、表示名がやたら長い場合なんかにいいですね。
いずれにしても [] で括るのを忘れると、正常に動作しません。「アプリケーションで問題が発生しました」とプリさんに怒られました。サーバースクリプトでミスったときの怒られ方といっしょだ。
5.スクリプトを使用した日付の加算、減算
計算式一択でいいんじゃね?と思いますが、一応スクリプトも。
4(7)の計算式をスクリプトでやってみました。
日付A:基準となる日付
日付B:計算結果を表示する項目
分類A:100:加算、200:減算を指定
数値A:基準値に加算、または減算する日数
分類Aが100の場合、日付B = 日付A + 数値A
分類Aが200の場合、日付B = 日付A – 数値A
の計算を行います。
スクリプトでは
$p.on と $p.set を使用しました。
$p.on(‘change’,項目,処理) と指定します。指定した[項目]の値に変化があったら、[処理]を実行します。
[処理]の部分の functionの中で $p.set を使用することで、日付Bの値を入力します。
$p.set(項目,入力する値)
[入力する値] のところに 日付値を計算する関数 fnAddDate を置きます。
そうすることで関数で計算した結果の戻り値が指定した [項目] に入力されます。
スクリプトで $p.on (○○が変わったら)を使うので、エディタでの各項目の「自動ポストバック」はオンになってなくても即時処理内容が反映されます。
スクリプトタブで以下のコードを入力します。出力先は「新規作成」「編集」としました。
$p.on('change','DateA',function(){
$p.set($p.getControl('DateB'),fnAddDate())
});
$p.on('change','NumA',function(){
$p.set($p.getControl('DateB'),fnAddDate())
});
$p.on('change','ClassA',function(){
$p.set($p.getControl('DateB'),fnAddDate())
});
function fnAddDate() {
const strDateA = $p.getControl('DateA').val();
const typeCalc = $p.getControl('ClassA').val();
const numDates = Number($p.getControl('NumA').val());
let dt = new Date(strDateA);
if (typeCalc === '100') {
dt.setDate(dt.getDate() + numDates);
} else {
dt.setDate(dt.getDate() - numDates);
}
return dt.getFullYear() + '/' + (dt.getMonth() + 1) + '/' + dt.getDate();
}
説明は省略しますが、ポイントは以下の点です。
- 数値Aの値は文字列で取れてくるので、Number関数で数値に変換しています。
- 日付Aは取得した段階では文字列なので、Dateオブジェクトで日付に変換しdtに代入しています。
- returnする日付値は文字列データに変換して、返却します。
- dt.setDate(dt.getDate() + numDates);
getDate でdtの日付を取得し numDates の数値を加算。
setDate で dt の日付を書き換えています。
【JavaScript入門】日付の加算・減算方法まとめ(月またぎ/うるう年)
エクセル頭で考えるとこんな感じ。。。
エディタでの項目の設定で、日付A等に規定値を設定しているので、上記のコードは項目値が空欄だった場合を考慮していません。
以下、日付A、分類A、数値Aが空欄だった場合のことも考慮したコードです。
日付Aが空欄の場合は処理を行わず、分類Aが空欄だった場合は「加算」、数値Aが空欄だった場合は「日数」=0 で計算を行うようにしています。
説明は省略しますが、コメントを入れています。
//DateA,NumA,ClassAのいずれかの値が変化したときに DateBの値をfnAddDateの計算結果で書き換える
$p.on('change','DateA',function(){ $p.set($p.getControl('DateB'),fnAddDate()) });
$p.on('change','NumA',function(){ $p.set($p.getControl('DateB'),fnAddDate())} );
$p.on('change','ClassA',function(){ $p.set($p.getControl('DateB'),fnAddDate()) });
//関数 fnAddDate
function fnAddDate() {
//DateAの値をstrDateAに代入(文字列yyyy/mm/dd、または空欄)
const strDateA = $p.getControl('DateA').val();
//ClassAの値をtypeCalcに代入(100 or 200 or 空欄)
const typeCalc = $p.getControl('ClassA').val();
//NumAの値をnumDatesに代入(文字列の数字をNumberで数値型に変換。空欄の場合は0となる)
let numDates = Number($p.getControl('NumA').val());
//DateAの値が空欄でない場合のみ実行。
//DateAの日付をもとに計算を行い算出結果を文字列データとしてstrDateBに代入
let strDateB = '';
if (strDateA !== '') {
//文字列の日付を日付型データに変換
let dt = new Date(strDateA);
//ClassAの値が200(減算)の場合、numDatesをマイナス値にする
if (typeCalc === '200') { numDates = - numDates }
//dtにnumDatesの日数を加算(マイナス値の場合は減算となる)
dt.setDate(dt.getDate() + numDates);
//計算結果の日付を文字列に変換して返却
strDateB = dt.getFullYear() + '/' + (dt.getMonth() + 1) + '/' + dt.getDate();
}
return strDateB;
}
javaScriptお勉強中のため、はたしてこれが正解なのかは不明ですが、結果的には欲しい結果は得られました。
関数 fnAddDate() の引数に項目値を持たせたいところだけど、$p.onのところが長くなっちゃうので、関数の中で項目値を取得するようにしました。
6.サーバースクリプトを使用した日付の加算、減算
同様に4(7)をサーバースクリプトでやってみた。
初心者コードだけど、期待した結果は得られた。
サーバースクリプトタブに以下のコードを入力します。
タイミングは「計算式の前」でやりました。
「計算式の後」「画面表示の前」「更新前」等でもいいのかもしれない。
項目 [日付A][数値A][分類A] の自動ポストバックをオンにした場合、タイミングを「計算式の前」「計算式の後」「画面表示の前」とすると、項目値を入力後すぐに計算結果が反映されます。
6のスクリプトとは趣旨をちょっと変えて、日付A、数値A、分類Aの3項目がすべて空欄ではない場合のみ、計算が実行される仕様としています。
try {
//レコードの値を取得
let dt;
if (model.DateA > 0) { dt = model.DateA; }
let numAddDay = model.NumA;
const tp = model.ClassA;
//dt,numAddDay,tp がundefinedまたはnullでない場合処理実行
if (dt && numAddDay && tp) {
//分類Aにより分岐し、加減算
if (model.ClassA !== '100') {
dt.setDate(dt.getDate() - numAddDay);
} else {
dt.setDate(dt.getDate() + numAddDay);
}
//日付Bの値を変える
model.DateB = dt;
} else {
model.DateB = '';
}
} catch(e) {
context.Log('error!');
context.Log(e.stack);
}
if (dt && numAddDay && tp) {…}
の部分は、変数値のすべてがtrueの場合{}内の処理に進みます。
項目値が空欄の場合、変数値がundefinedまたはnullとなります。その場合falseということになるらしい。
ただし、日付値は空欄でも変数値に値が入る場合があり(編集画面で入力済の日付を削除し空欄にした時、1899年12月29日15:00が入った)、0より大とすることで変な日付が入ることを回避しています。
日付値空欄で1900/1/0になるのはわかるけど、1899/12/29 15:00って。。。?ひょっとしてうちの環境、時間が9時間ずれてる…? プリザンターの時刻表記はあってるけど、プリなまでやってたRDBMとプリザンターの時刻ロケールが違うというやつかしら。
時刻のずれはプリなまの13回でやってますね。
https://pleasanter.org/lp/pleanama
家のプリザンターだからとりあえずスルー。。。
なお、try { … } catch(e) { … } とすると、tryの{}内でエラーになったときに、catchの{}のほうに行ってくれます(行ってくれない時もある)。
context.Log(e.stack); とすると、スタックトレース、エラー発生個所の詳細をトレース(追跡)する情報を出してくれるそうです。(見てもわかんないことが多いんだけど。。。一応出してみる)
その前に context.Log(‘error!’) と出力しているのは、コンソールにぺろぺろ出たものがエラーで出たものなのか、普通に出たものなのかわからないので、「エラーが出たんだ」という目印として出力するようにしています。初心者なもので。
【JavaScript入門】try…catchの使い方と例外処理のまとめ!
7.最後に
VBAをやり始めるまで、約15年間はエクセルを関数とピボットだけでやってきた。なので、関数の組み合わせの妙技で目的を達成する、ということに並々ならぬ情熱を注いできたわけで関数が私の原点。
で、プリザンターでも関数が使えるようになったことがうれしい。組み合わせの妙技と発想の転換でスクリプト書かずともいろんなことができそう。
プリなまでも計算式(拡張)のことやってましたよ。
https://pleasanter.org/lp/pleanama
プロセスでも使えるようになったとか。バージョン上げなきゃ。。。
9.参考文献、記事
プリザンター公式
テーブルの管理:計算式
テーブルの管理:計算式(既定)
テーブルの管理:計算式(拡張)
テーブルの管理:エディタ:項目の詳細設定:自動ポストバック
テーブルの管理:エディタ:項目の詳細設定:自動ポストバック時に返却する項目
プリなまアーカイブ
参考にさせていただいた記事
ポストバックってなあに?
[ASP.NET]ポストとポストバックの違いは?
【プリザンター】 第183回)X日後の日付を計算させるスクリプト
【JavaScript入門】日付の加算・減算方法まとめ(月またぎ/うるう年)
【Javascript入門】数値⇔文字列の変換(toString/Number/parseInt)
日付処理を使い倒す! JavaScriptのDate活用法を徹底解説
【JavaScript入門】try…catchの使い方と例外処理のまとめ!
ありがとうございました。
コメント