
Googleスプレッドシート チェックした行を保護(ロック)するGASの作り方
前回に続いて GoogleAppsScript(通称GAS)による Googleスプレッドシート操作ネタです。
今回はチェックボックスの ON/OFFをトリガーとしてGASを実行。チェックした行を保護、チェックを外すと保護解除という処理を通じて、シンプルトリガーやボタン等で割り当てた通常のGAS実行と違う インストーラブルトリガーの挙動について書いていきます。

基本的には管理者が作ってユーザーに使ってもらうスプレッドシートの運用イメージです。
前回の noteは 「Googleスプレッドシート 編集日時と編集者をログシートに記録するGASの作り方」を書きました。
GASでチェックした行を保護(ロック)する のゴール

今回のゴールは ↑ こんな感じをイメージしています。
A列のチェックボックスにチェックを入れる(ONにする)と、その行が保護されて自分を含め編集者は触れなくなる。
チェックを外す(OFFにする)と、行の保護は解除されて再び編集可能になる。
こんなことがやりたい!って人には参考になるかと思います。
ただし、前提として 上は mirがオーナーのスプレッドシートではなく、他の人がオーナーで mirは編集者として共有されている状態となります。後で再度触れますが、どうやってもオーナーの編集は制限できません。
また、複数のセルを一括編集(一括チェック)するケースは対象外としています。
ついでに、チェックボックスを消されたり、チェックボックスのセルに通常の文字を入力、コピペされちゃった場合のリカバリー機能もつけておきましょう。

このように、チェックが入っていたチェックボックスの場合は、しっかりチェックが入った状態に復元できるのが理想的ですね。
スプレッドシートの保護の理解
GAS スプレッドシートを保護する Protectionクラスの理解
GASのインストーラブルトリガーの理解
e(イベント)から取得できる情報の理解
【オマケ】チェックを入れたら色を変える条件付き書式の理解
今回の仕組みを実現するには、これらの理解が必要になります。
コードは最後に書いてありますが、出来れば途中を読み飛ばさずに意味を理解した上でコードを使いましょう。
Googleスプレッドシート 保護の理解

Excelの保護は ちょっと癖があり、シートを保護して編集させたい箇所のロックを外すという操作なんですよね。
だから、最初慣れないうちは特定のセルだけを保護(ロック)したい時に、あれ「ロック」のチェックをどうするんだっけ?ってなりがちです。
それに対してGoogleスプレッドシートはシートの保護と別に 「範囲の保護」という機能があるので、一見わかりやすいんですが・・・。
こちらも実は癖が強く、出来ないこともあって厄介だったりします。
Googleスプレッドシートは オーナー と自分 の編集を制限できない

Googleスプレッドシートで特定のセル範囲を保護したい場合、保護したい範囲を選択した状態で
右クリック > セルでの他の操作項目を表示 > 範囲を保護
と進みます。(メニューの データから「シートと範囲の保護」から入ってもOK)
これはわかりやすくて良いです。
これとは別にシートを保護して、特定のセルだけ保護を外す(編集可能とする)という保護のかけ方もありますが 今回は割愛します。

選択した範囲がセットされた状態で、サイドバーで保護の設定が表示されます。ここで「権限を設定」を押します。

設定できる保護のタイプは 2種類あって
警告を表示する
編集できるユーザーを制限する
となっています。通常は 「編集できるユーザーを制限する」の方を選択します。
この「編集するユーザーを制限する」は、さらに
自分のみ(自分とスプレッドシートオーナーのみ)
カスタム
別の範囲から権限をコピー
の3つから選択ができます。
自分がスプレッドシートのオーナーの場合は、一番上の選択肢は「自分のみ」ですが、編集者だった場合は「自分とスプレッドシートオーナーのみ」と表示が変わります。

つまり 絶対に 編集させたくない(編集したくない)セルがあっても、「範囲の保護」機能では オーナーの編集は制限できないということです。
さらにオーナーが別にいて、自分は編集者として共有メンバーだった場合、保護をかけようとしても オーナーと自分を制限することが出来ません。

カスタムを選択した場合も、編集できるユーザーを選択するチェックボックス(チェックが付いてる人だけが編集できる)では、自分やオーナーのチェックは外すことが出来ません。
これは、Googleスプレッドシートの保護が
保護したセルを編集する権限 = その保護を解除する権限
である為です。
共有を前提としてるんで仕方ないとは思いますが、普段は閲覧のみでよくて誤操作が怖いから自分の編集も制限しておきたい、って時に困るんですよね。
といっても、仕様なのでこれは諦めるしかないです。
が、 オーナーは無理ですが、今回はGASを使って編集者であるユーザー自身を編集できないようにロックする方法を紹介します!
保護の「警告を表示する」は役に立たない

では、2つの保護のうちの1番目の 「この範囲を編集する時に警告を表示する」を設定した場合はどうか?
こちらはオーナーや 編集者(自分自身)にも適用されます。
オーナーであっても、保護を設定したのが自分自身であっても、編集しようとすると

このように警告を出すことが出来ます。
しかし、このモーダルダイアログが イマイチ役に立ちません。
なぜなら、上の選択肢の「OK」が選択された状態のダイアログが表示されるだけなんで

このように、なんとなく Enterを押すだけで突破出来てしまいます。
ユーザーとしては「あれなんか表示された? ま、いっか。」って感じで編集が出来ちゃいます。ディフェンスがザルすぎですw
せめてダイアログ内で選択しないと編集できないようにして欲しいんですよね。
というわけで、保護は基本的には 「編集できるユーザーを制限する」を使うことになります。
GAS スプレッドシートを保護する Protectionクラスの理解
それではGoogleスプレッドシートの保護をGASから操作する方法を見ていきましょう。
こちらも、今回は 特定のセル範囲を保護、解除するGASの説明となります。シート保護については触れていません。
範囲を保護する GAS

セル範囲に対して保護を適用する為に、Rangeクラスに protect() というメソッドが用意されています。
たまに、これだけで保護が出来るものと勘違いして「GASで保護したはずなのにユーザーが編集出来てしまいます」といった質問を見かけますが、公式にも
範囲の編集者のリストを変更するまで、権限はスプレッドシート自体の権限を反映します。つまり、範囲は実質的に保護されません。
と書いてある通り、これだけでは保護とはなりません。
ちょっとわかりづらいですね。具体例を見てみましょう。
たとえば選択したセルを保護(設定)するコードは、
function rangeLock() {
SpreadsheetApp.getActiveRange().protect();
}
このように1行コードで書けます。これを実行すると

このように一見 保護された風ですが、これは まったく制限が設定されていない(中身がない)保護の器のようなものです。
これだけでは意味がありません。
この状態では、編集者として共有しているメンバーはもちろんのこと、スプレッドシートを リンクを知っている全員が編集できる状態で共有していれば、誰でも編集出来ちゃいます。
この保護(Protection)の設定、操作するための Protectionクラスが用意されています。
protect()メソッドで保護を生成した上で Protectionクラスのメソッドを用いて 保護の設定をしたり、既に設定されている保護の情報を取得したり、保護を解除したりします。
範囲を保護する定番コード
ぶっちゃけ、範囲保護と保護解除の よく使う コードは定番なんで、深く理解せず 型として覚えちゃってもいいかもしれません。
まず、選択した範囲を保護(自分とオーナー以外は編集できない状態に)するコードは
//選択したセル範囲を保護するコード
function rangeLock() {
const range = SpreadsheetApp.getActiveRange();
const protection = range.protect();
protection.removeEditors(protection.getEditors());
}
これです。
range.protect() で作成した保護を protection として、そこからまず protection.getEditors() で現状この範囲を編集できるユーザーを取得、
それをそのまま使って
protection.removeEditors(protection.getEditors())
とすることで、現在の編集者を可能な限り除外(編集対象外)としています。
もちろんオーナー、そして自分(スクリプトの実行者)が編集者だった場合は自分も含め、保護範囲の編集者から remove(除外)することは出来ません。


編集者が実行した場合は、自分ともう1人(オーナー)だけが編集できる状態となったのがわかりますね。

このように通常のエディタからの GAS実行では、編集者であっても 自分を制限することは出来ないってことです。
範囲の保護を解除する定番コード
続いて範囲の保護の解除ですが、こちらは少し厄介です。
GASでは 範囲(Range)から、そのセルが保護されているかどうかを判別したり、そのセルに設定されている保護を取得したりするメソッドがありません。
面倒ですがSheetクラスの getProtections(type) というメソッドを利用して
type を範囲の保護を指定して、まずシートに設定されている保護を全て取得
した上で、for や forEach で 全保護の配列から 1つ1つの保護を取り出して処理。(もしくは 取り出した保護の 範囲を確認して処理)
という手順を踏みます。
開いているシートに設定されている 範囲保護を全て解除する定番コードはコチラ。
//開いているシートの保護を全て解除するコード
function removeRangeLock(){
const sheet = SpreadsheetApp.getActiveSheet();
const protections = sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE); //シートの範囲保護を全て取得
protections.forEach(protection => {
console.log(protection.getRange().getA1Notation()); //保護範囲をログに書き出し
protection.remove(); //保護を解除
})
}
動かしてみると

このように、たくさんあった範囲保護が一気に解除されます。
もちろん、スクリプトの実行者が 編集できない保護範囲があった場合、そちらは解除することは出来ません。
スクリプトが出来ること = スクリプト実行者の出来ること
権限に関しては、このようになっていることに注意しましょう。
それでは、編集者が 範囲に対して 自分が制限された(触れられない)保護を設定したり、制限されている保護範囲を解除するにはどうしたらよいか?
その答えが、インストーラブルトリガーです。
GASのインストーラブルトリガーの理解

前回も登場したGASのインストーラブルトリガーは、onOpen()や onEdit() のように関数名をこれに設定すれば自動で 開いた時、編集した時に発動する シンプルトリガーと違って、自分で設定するトリガーです。
前回触れた Gmail」送信などの「やれること」(権限)の違い以外にも、シンプルトリガーとインストーラブルトリガーでは大きな違いがあります。
トリガータイプによる実行者の違い

簡単にまとめると、このようになっています。
ここでポイントとなるのが 5番の 「スクリプトの実行者」の違いです。
GASの実行方法としては、エディタからの実行やカスタムメニューを表示して、メニューにスクリプトを割り当てて実行、または 図形描写のボタンにスクリプトを割り当てて、ボタンを押したら実行あたりが定番です。
しかし、これらはいずれもスクリプトの実行者はユーザー自身であり、初回に スクリプトの認証が必要になります。

今回のようにチェックボックスをボタン替わりとして、シンプルトリガーで スクリプトを実行した場合、初回の許可は必要ありませんが、やはりスクリプトの実行者はユーザー自身となります。
つまり、これでは 自分(編集者)を制限した 保護をかけることは出来ません。
一方インストーラブルトリガーは、トリガーをセットした人がスクリプトの実行者となります。
オーナーがトリガーをセットした場合は、オーナー権限でスクリプトが実行される ってことです。
これだと、自分(編集者)が、編集した(チェックボックスを操作した)時に実行される GASで、自分自身を 保護範囲の編集者から除外することが可能となります。
これは初期のnote タイムスタンプネタで ユーザーがタイムスタンプを消せないようにする方法でも紹介しています。
少し近づいてきましたね。
e(イベント)から取得できる情報の理解
前回も登場しましが、トリガーで実行される関数は e(イベント)を引数とすることで、トリガーアクションに関連する情報を取得することができます。

eから 編集時の取得情報を取得しよう
今回使うのは
function rowLock(e) {
const range = e.range; //編集されたRangeオブジェクト
const value = e.value; //編集で書き込まれた値
const col = e.range.columnStart; // 編集されたセル範囲の(一番左の)列番号
const row = e.range.rowStart; //編集されたセル範囲の(一番上の)行番号
const sheet = e.range.getSheet(); //編集されたシートオブジェクト
const sheetName = sheet.getName(); //編集されたシート名
console.log(value,col,row,sheetName); //試しにログに出力してみる
}
この辺りの情報です。

トリガーをセットして取得できる情報を確認してみましょう。

チェックを入れた時の value は "TRUE"という文字列になっており、
1列目 の 4行目が編集されたセル、シート名は「販売管理」という情報が取得できていますね。
これらの情報を使って if で処理を分岐させ、チェックした行を保護するコードを書いてみましょう。
今回は 対象のシート名を 「販売管理」、チェックボックスのある列 を A列(1列目)、保護する範囲は チェックされた行の B列より左(A列を除く)、見出し行は1行目とします。

/** @OnlyCurrentDoc */
const targetSheetName = "販売管理"; //対象シート名
const checkColNum = 1; //チェックボックスの列番号
const rockCol = "B" //保護範囲の一番左の列
const headerRow = 1 //見出し行の行番号
ちなみに今回は、自分のスプレッドシートの操作のみなんで、
/** @OnlyCurrentDoc */
これを入れています。これを入れておくと、初回のスクリプトの認証が少しだけ優しくなりますw
※ともかつさんのノートに詳しく書かれています
チェックボックスを e.oldValueで取得した時の注意点

もう1つ、e(イベント)から oldValueを使って、編集前のセルの値を取得しておきましょう。
これは、チェックボックスが Delteで消されてしまったり、通常のテキスト入力やコピペで消されてしまった場合、自動で復元したいのですが、その際にチェックが入っていた場合は チェックが入った状態で復元させたいからです。
変更前の値を e.oldValue で取得することで、そこにチェックが入っていたか否かを判別します。
しかし、チェックボックスの e.oldValueは ちょっと注意点があります。

↑ こちらは 編集時トリガーで console.log(e.oldValue, e.value) で、チェックボックスの変更前後の値を取得したものです。
チェックあり → チェックなし の場合、
true → FALSE
ですが、
チェックなし → チェックあり の場合、
false → TRUE
となっています。
なぜか チェックボックスの true,falseの 真偽値(Boolean値)は、
e.value で取得すると 大文字 (TRUE, FALSE)
e.oldValue で取得すると 小文字 (true, false)
となるんです。ここは注意です。
GASでチェックした行を保護するコードを書いてみよう
それでは実際に処理をコーディングしていきましょう。
スプレッドシートのオーナーがGASの作成、トリガー設定を行い、ユーザー(編集権限の共有メンバー)が使うことを想定しています。
チェックした行を保護するコード
/** @OnlyCurrentDoc */
const targetSheetName = "販売管理"; //対象シート名
const checkColNum = 1; //チェックボックスの列番号
const rockCol = "B" //保護範囲の一番左の列
const headerRow = 1 //見出し行の行番号
function rowLock(e) {
const range = e.range; //編集されたRangeオブジェクト
const value = e.value; //編集で書き込まれた値
const col = e.range.columnStart; // 編集されたセル範囲の(一番左の)列番号
const row = e.range.rowStart; //編集されたセル範囲の(一番上の)行番号
const sheet = e.range.getSheet(); //編集されたシートオブジェクト
const sheetName = sheet.getName(); //編集されたシート名
//編集が対象シート以外、またはチェックボックス列以外、見出し行以上の時は終了
if(sheetName != targetSheetName || col != checkColNum || row <= headerRow) return;
if(value == "TRUE"){ //チェックの時は保護の実行
const targetRange = sheet.getRange(rockCol+row+":"+row); //保護する範囲
const protection = targetRange.protect(); //範囲を保護して
protection.removeEditors(protection.getEditors()); //オーナー以外(自分も含め) 全員の編集権限を削除
}
}
まずは、こちら。とりあえず大丈夫ですよね?
if(sheetName != targetSheetName || col != checkColNum || row <= headerRow) return;
こちらで、シート名が対象外(対象外のシートを編集した)時、または(||)対象のシートの編集であっても1列目の編集ではない、または 見出し行より上(行番号が見出し行以下)。
この時は return で終了(これ以降のコードは実行されない)としています。
if(value == "TRUE")
さらに、ここで
編集された後の値が TRUE なら = チェックボックスにチェックを入れたら
を条件として、編集された行の B列以降の範囲
rockCol+row+":"+row ・・・ たとえば4行目の場合は B4:B
を保護としています。
保護部分のコードは 先ほど紹介した定番そのままですね。
チェックを外した行の保護を解除するコードを追加
/** @OnlyCurrentDoc */
const targetSheetName = "販売管理"; //対象シート名
const checkColNum = 1; //チェックボックスの列番号
const rockCol = "B" //保護範囲の一番左の列
const headerRow = 1 //見出し行の行番号
function rowLock(e) {
const range = e.range; //編集されたRangeオブジェクト
const value = e.value; //編集で書き込まれた値
const col = e.range.columnStart; // 編集されたセル範囲の(一番左の)列番号
const row = e.range.rowStart; //編集されたセル範囲の(一番上の)行番号
const sheet = e.range.getSheet(); //編集されたシートオブジェクト
const sheetName = sheet.getName(); //編集されたシート名
//編集が対象シート以外、またはチェックボックス列以外、見出し行以上の時は終了
if(sheetName != targetSheetName || col != checkColNum || row <= headerRow) return;
if(value == "TRUE"){ //チェックの時は保護の実行
const targetRange = sheet.getRange(rockCol+row+":"+row); //保護する範囲
const protection = targetRange.protect(); //範囲を保護して
protection.removeEditors(protection.getEditors()); //オーナー以外(自分も含め) 全員の編集権限を削除
} else if (value == "FALSE") { //チェックをオフした時は保護の解除
const protections = sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE); //シート内のセルの保護を全て取得
protections.forEach(protection => {
const pRow = protection.getRange().getRow(); //保護されている範囲の行番号を取得
if (pRow == row) { //保護された行番号がチェックを外した行と同じ場合保護を解除
protection.remove();
}
});
}
続いて、先ほどのコードに チェックを外した時は、その行の保護を解除という処理を追記します。
else if (value == "FALSE")
これで チェックを外したと判断しています。
シートに設定されている範囲保護を全て取得し、forEach で protections から保護を一つ ずつ取り出して(protection としている)
protection.getRange().getRow() で、保護範囲の先頭行( = 今回は保護範囲は1行単位なので保護の行)を取得し、それが
if (pRow == row) で 編集された(チェックを外した)行と一致した場合、 保護を解除 removeするとしています。
こちらは条件を加えて、先ほどの定番コードを少し応用してますね。
チェックボックスを潰してしまった時の処理を追加
/** @OnlyCurrentDoc */
const targetSheetName = "販売管理"; //対象シート名
const checkColNum = 1; //チェックボックスの列番号
const rockCol = "B" //保護範囲の一番左の列
const headerRow = 1 //見出し行の行番号
function rowLock(e) {
const range = e.range; //編集されたRangeオブジェクト
const value = e.value; //編集で書き込まれた値
const col = e.range.columnStart; // 編集されたセル範囲の(一番左の)列番号
const row = e.range.rowStart; //編集されたセル範囲の(一番上の)行番号
const sheet = e.range.getSheet(); //編集されたシートオブジェクト
const sheetName = sheet.getName(); //編集されたシート名
//編集が対象シート以外、またはチェックボックス列以外、見出し行以上の時は終了
if(sheetName != targetSheetName || col != checkColNum || row <= headerRow) return;
if(value == "TRUE"){ //チェックの時は保護の実行
const targetRange = sheet.getRange(rockCol+row+":"+row); //保護する範囲
const protection = targetRange.protect(); //範囲を保護して
protection.removeEditors(protection.getEditors()); //オーナー以外(自分も含め) 全員の編集権限を削除
} else if (value == "FALSE") { //チェックをオフした時は保護の解除
const protections = sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE); //シート内のセルの保護を全て取得
protections.forEach(protection => {
const pRow = protection.getRange().getRow(); //保護されている範囲の行番号を取得
if (pRow == row) { //保護された行番号がチェックを外した行と同じ場合保護を解除
protection.remove();
}
});
}else{ //チェックオンオフ以外は チェックボックスを潰したと判定しチェックボックスを復活させる
range.insertCheckboxes();
console.log(e.oldValue);
if(e.oldValue == "true"){ //もともとチェックが入っていた場合はチェックを復元
range.check();
}
}
}
最後に、チェックボックスを誤って消してしまった(違う文字を入れてしまった)時の処理を追記します。
ここまで対象範囲が編集され、その値である value が TRUEの時、FALSEの時の処理を書いてきたので、これを通過してきたということは対象範囲(チェックボックス)が 潰されたと判断します。
range.insertCheckboxes() で、まずは編集されたセルにチェックボックスを復活させます。
if(e.oldValue == "true")
さらに、元々チェックが入っていた場合、 e.oldValue が "ture"だった場合
は、check() でチェックを入れた状態に戻します。
この辺りのチェックボックス関連のメソッドは、過去noteのチェックボックスの回で詳しく紹介しています。
コードが完成しました~。
【オマケ】チェックボックスにチェックを入れたら 条件付き書式で行の色を変更する

これは GASではなく条件付き書式なんで オマケみたいなもんですが、保護されたことが視覚的にわかるように、A列がチェックされたら同じ行の B~G列の色を変える(グレーアウト風)という設定をしておきましょう。
チェックボックスを条件にした条件付き書式は簡単

これはぶっちゃけ簡単です。カスタム数式は使いますが 2行目以降を対象とするのであれば、
=$A2
これだけです。
条件付き書式のカスタム数式は TRUEとなる時に 書式が適用されるので、
チェックが入っている = TRUE
となるチェックボックスは、そのまま利用できます。
あとは行全体を色付けするので、列だけ絶対参照で固定すればOK。これで完成です。
※ 交互の書式設定、日付やプルダウンの列へのデータの入力規制、VLOOKUP等の 金額表示の式なんかも仕込んでますが、それらの説明は割愛します。
【完成版】GASでチェックした行を保護するコード
/** @OnlyCurrentDoc */
const targetSheetName = "販売管理"; //対象シート名
const checkColNum = 1; //チェックボックスの列番号
const rockCol = "B" //保護範囲の一番左の列
const headerRow = 1 //見出し行の行番号
function rowLock(e) {
const range = e.range; //編集されたRangeオブジェクト
const value = e.value; //編集で書き込まれた値
const col = e.range.columnStart; // 編集されたセル範囲の(一番左の)列番号
const row = e.range.rowStart; //編集されたセル範囲の(一番上の)行番号
const sheet = e.range.getSheet(); //編集されたシートオブジェクト
const sheetName = sheet.getName(); //編集されたシート名
//編集が対象シート以外、またはチェックボックス列以外、見出し行以上の時は終了
if(sheetName != targetSheetName || col != checkColNum || row <= headerRow) return;
if(value == "TRUE"){ //チェックの時は保護の実行
const targetRange = sheet.getRange(rockCol+row+":"+row); //保護する範囲
const protection = targetRange.protect(); //範囲を保護して
protection.removeEditors(protection.getEditors()); //オーナー以外(自分も含め) 全員の編集権限を削除
} else if (value == "FALSE") { //チェックをオフした時は保護の解除
const protections = sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE); //シート内のセルの保護を全て取得
protections.forEach(protection => {
const pRow = protection.getRange().getRow(); //保護されている範囲の行番号を取得
if (pRow == row) { //保護された行番号がチェックを外した行と同じ場合保護を解除
protection.remove();
}
});
}else{ //チェックオンオフ以外は チェックボックスを潰したと判定しチェックボックスを復活させる
range.insertCheckboxes();
console.log(e.oldValue);
if(e.oldValue == "true"){ //もともとチェックが入っていた場合はチェックを復元
range.check();
}
}
}
改めて、こちらが 完成したコードです。
↓ トリガーを編集時で設定しています。

完成したコードをユーザー側からテストする
ユーザーアカウントに共有して、ユーザー側で動かしてみましょう。

チェックで保護が機能してますね。チェックを入れた自分も編集が出来なくなり、保護は「表示専用」となっています。
また、チェックボックス(A列)以外の他のセルを編集しても保護に影響がないのもわかりますね。
ちなみに チェックボックスを入れた保護された動作を 「戻るボタン」またはショートカット の Ctrl + Z で 無かったことにしようとしても

このようにエラー表示が出て、保護設定前に戻すことは出来ません! しっかり保護されています。

チェックを外した際の保護解除も出来てます。
GAS特有の処理のもったり感はあるので、チェックの切り替えをしてすぐだと、まだ保護の切り替えが反映されていないこともあるので、そこは注意。

チェックボックスを消してしまった、文字を入れて潰してしまった際の復元処理も想定通り動きました。
チェックが入っていた場合は、チェックボックス復元後にチェックも入ります。
無事、やりたかった動作ができました~。
アレンジコード(チェックで日時を B列に書き込み、チェック後はチェックを外せなくする)
/** @OnlyCurrentDoc */
const targetSheetName = "販売管理"; //対象シート名
const checkColNum = 1; //チェックボックスの列番号
const rockCol = "A" //保護範囲の一番左の列 チェックボックスもロック
const headerRow = 1 //見出し行の行番号
function rowLock(e) {
const range = e.range; //編集されたRangeオブジェクト
const value = e.value; //編集で書き込まれた値
const col = e.range.columnStart; // 編集されたセル範囲の(一番左の)列番号
const row = e.range.rowStart; //編集されたセル範囲の(一番上の)行番号
const sheet = e.range.getSheet(); //編集されたシートオブジェクト
const sheetName = sheet.getName(); //編集されたシート名
//編集が対象シート以外、またはチェックボックス列以外、見出し行以上の時は終了
if(sheetName != targetSheetName || col != checkColNum || row <= headerRow) return;
if(value == "TRUE"){ //チェックの時は保護の実行
const now = new Date();
sheet.getRange(row,2).setValue(now); // タイムスタンプ
const targetRange = sheet.getRange(rockCol+row+":"+row); //保護する範囲
const protection = targetRange.protect(); //範囲を保護して
protection.removeEditors(protection.getEditors()); //オーナー以外(自分も含め) 全員の編集権限を削除
} else if (value == "FALSE") { //チェックをオフした時は保護の解除
const protections = sheet.getProtections(SpreadsheetApp.ProtectionType.RANGE); //シート内のセルの保護を全て取得
protections.forEach(protection => {
const pRow = protection.getRange().getRow(); //保護されている範囲の行番号を取得
if (pRow == row) { //保護された行番号がチェックを外した行と同じ場合保護を解除
protection.remove();
}
});
}else{ //チェックオンオフ以外は チェックボックスを潰したと判定しチェックボックスを復活させる
range.insertCheckboxes();
console.log(e.oldValue);
if(e.oldValue == "true"){
range.check();
}
}
}
今回作成したコードを少しアレンジして 冒頭部分を
const rockCol = "A" //保護範囲の一番左の列 チェックボックスもロック
とすると、ユーザー(編集者)側ではチェックボックス解除が出来なくなります。
つまり、オーナーに対応してもらうわないと 一度確定した(保護した)範囲は編集できない状態となるわけです。
さらに、
if(value == "TRUE"){ //チェックの時は保護の実行
const now = new Date();
sheet.getRange(row,2).setValue(now); // タイムスタンプ
チェック時の保護の前に このように タイムスタンプを B列に自動入力するコードを追記すると、チェックして保護(確定)した日時を記録できます。

報告系のスプレッドシートなどで、確定後は本人に変更させたくないといった厳密な運用をしたい場合に使えるかと思います。
まぁここまでやるなら、スプレッドシートは触らせずGoogleフォーム等で運用した方がいいような気もしますが。
コードがわかればアレンジも出来る
今回は、GASを使って 編集者がチェックした際に、チェックボックスがある行を保護し、チェックした編集者自身も操作できなくさせる方法を紹介しました。
ちなみにこちら、編集時トリガーと チェックボックスを組み合わせたスクリプトなんで、スマホアプリ版でも使えます。

チェックを入れたら、行が 表示専用となっているのがわかりますね。
スマホでのGAS実行は、チェックボックスと GAS(編集時トリガー)の組み合わせが定番です!
スタートはコピペで使う、AIに作成してもらうでもよいですが、やはりそれだけでは限界があります。
コードがわかれば、アレンジもできる。(元気ですか~!)
コードの意味を理解して、是非自分のもの(自分のスキル)としてください!!
次回は GAS以外でなんか書きます。
【有料コンテンツ】チェックボックスによる 行の保護を仕込んだスプレッドシートを用意しました。
最後に コードのコピペすら 面倒だからコピーしてすぐ使えるシートが欲しい!って人向けに、 有料コンテンツとして今回の チェックボックスで行が保護できる Googleスプレッドシート を配布いたします。
解説は全て無料 部分に記載しているので、有料コンテンツに さらなる情報はありません。
面倒だから 出来上がったスプレッドシートを使いたいって人
単に mir を応援したいんで 課金しますという足ながおじさんな人
無料部分の記事が参考になった、面白かったんで 支援しますって人
こんな方だけ 有料コンテンツをご利用ください。
多少なりとも 有料コンテンツ利用者がいると、モチベーションにもつながりますし、執筆にも力が入りますので感謝感謝です。
なお、有料コンテンツで配布する Googleスプレッドシートには解説では触れていない
商品をプルダウンで選択すると 自動でマスタから 単価を読み込み、数量を入れると 自動で合計金額を計算する数式
データの入力規制(日付のみ、プルダウン)
ボタン一つでトリガーを設定できる トリガークラスの コード
https://developers.google.com/apps-script/reference/script/trigger?hl=ja
これらも設定しております。
よろしければ 是非!
↓以下、有料コンテンツです。
ここから先は
¥ 200
チップ大歓迎です。やる気がアップしますw