2023年 2月 の投稿一覧

GASでシートの入社日や退職日に合わせて自動でメールを送信する事例

0.はじめに

従業員の入社や退職に際して自動的に連絡をしたい、
そんな時に活用できる例をご紹介します。

1.一覧スプレッドシートについて

今回使用するスプレッドシートはこんな形式だとします。
列構成を変えたい場合などもコードを少し変更すれば対応可能ですので柔軟に考えてください。
1行に1名の情報が入っていると楽です。

入社管理スプレッドシート

 

2.設定シートについて

メールの宛先や件名、内容などを簡単に管理できるように
スプレッドシート上に設定情報を持たせておきます。

メール設定シート

これをやると簡単に設定変更が可能になり汎用的なコードに近づきますが、
必要に応じてシートの編集権限を絞る、等の工夫が必要な場合もあるかと思います。

3.コード

ではこれらの内容を取得して、送信に繋げていきます。

//最初にライブラリdayjsを追加しておくこと
//スクリプトIDは1ShsRhHc8tgPy5wGOzUvgEhOedJUQD53m-gd8lG2MOgs-dXC_aCZn9lFB

/**
 * メイン処理
 * 入社日が本日のものだけを対象にするため、日次トリガーを設定する必要がある
 */
function sendEntryMail() {
  //シート情報を取得
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const targets = getEntryDatas(ss);
  if(targets.length == 0) return; //本日入社が無ければ終了
  
  //メール設定を取得
  const mailSetting = getMailSetting(ss, "入社");
  
  //1人ずつ送信
  for(let person of targets){
    //送信設定情報を作成
    let mailArgs = { cc: mailSetting.Cc,
                     bcc: mailSetting.Bcc,
                     from: mailSetting.FromAddress,
                     name: mailSetting.FromName,
                     htmlBody: mailSetting.Body.replace(/%対象者%/g,person.Name) //置換
                    };
    //送信
    GmailApp.sendEmail(person.Mail,mailSetting.Subject,"",mailArgs);
  }
}

/**
 * 本日入社のデータを取得する
 * @param {object} ss スプレッドシートオブジェクト
 * @return {object} todayEntry 本日入社のデータ 無ければ空配列 
 */
function getEntryDatas(ss){
  //スプレッドシートデータを取得
  const sh = ss.getSheetByName("入社");
  const datas = sh.getRange("A2:G").getValues().filter(x => x[0]);
  const obj = datas.map(x => new Entry(x));
  
  //本日入社分にフィルタ
  const todayEntry = obj.filter(x => dayjs.dayjs(x.Date).isSame(dayjs.dayjs(),"day"));
  return todayEntry;
}

//入社者のデータを整理するためのクラス
class Entry{
  /**
   * @param {array} row 入社シート1行分の配列
   */
  constructor(row){
    this.Date = row[0];
    this.Number = row[1];
    this.Mail = row[2];
    this.Name = row[3];
    this.Div = row[4];
    this.Prace = row[5];
    this.Notes = row[6];
  }
}

/**
 * 引数で指定したメール設定を返す
 * @param {object} ss スプレッドシートオブジェクト
 * @param {string} type 設定シートのどの項目を取得するか文字列で指定
 * @return {object} typeに応じた設定object
 */
function getMailSetting(ss,type){
  const sh = ss.getSheetByName("メール設定");
  const datas = sh.getRange("A2:H").getValues().filter(x => x[0]);
  const obj = datas.map(x => new MailSetting(x));

  const targetObj = obj.find(x => x.Type == type);
  return targetObj;
}

//メール設定を整理するためのクラス
class MailSetting{
  /**
   * @param {array} row メール設定シート1行分の配列
   */
  constructor(row){
    this.Type        = row[0];
    this.Cc          = row[1];
    this.Bcc         = row[2];
    this.FromAddress = row[3];
    this.FromName    = row[4];
    this.Subject     = row[5];
    this.Body        = row[6];
    this.Notes       = row[7];
  }
}

ポイントをいくつか挙げていきます。

3-1.day.jsについて

ライブラリ、day.jsを使っています。
使わなくても日付比較はできますが、使うと非常に楽ですので使用をオススメします。

使い方は、エディタ左側のライブラリのところで「+」を押し
下記のスクリプトIDを入力、追加してください。

1ShsRhHc8tgPy5wGOzUvgEhOedJUQD53m-gd8lG2MOgs-dXC_aCZn9lFB

day.js公式

3-2.トリガーについて

本日入社者がいないかチェックしてメール送信する仕組みですので、
毎日起動する必要があります。

社風によって適切な時間帯が異なると思いますので
お好きな時間帯を設定してください。

3-3.クラスとmapについて

スプレッドシート情報を取得した後、mapの中でnewしています。
各行の内容を用いてインスタンスオブジェクトを作成することで
効率的にキー付きの扱いやすいオブジェクトを作成できます。

非常に便利で、メイン処理を汚さないコードを書きやすくなります。
今回のコードはかなりシンプルで分かりやすいため、
クラスの理解がまだの方はじっくりと動きを確かめてみてください。

3-4.エラー処理について

この手の処理はあまりサーバー系のエラーが起きないので何もしていません。
が、こういう油断が足元をすくうのでtry catchで囲っておくべきです。

「サーバーエラーの場合try catchでも拾えないじゃない」と思う方は、
実行ログを作成して履行チェックを実施するGASを作成してください。
履行チェックGASは下記記事で使っていますので、よければ参照してみてください。
フォームのデータを取得できませんでした。のエラーに対処した話

4.さいごに

いかがでしたでしょうか。

私の会社ではシート上にもっと情報が多く、処理項目も多いため
メール送信部分だけを切り出してご紹介しました。

日次に応じてひな形に沿った対応を行う、という業務は
非常に自動化しやすい業務ですので、見つけたらチャンスです。

ぜひ活用してみてください。

GASでGoogleグループのエイリアス一覧を取得するコードと解説

0.はじめに

前回記事、
GASでGoogleグループのメンバー一覧を取得するコードと解説
のエイリアスバージョンです。

Admin画面からエイリアス設定を参照するためには、
少し操作ステップが必要になります。
スプレッドシートに一覧化しておけば参照が楽になり、
また編集履歴から古い情報も確認可能になります。

今回もドメイン名とシート名以外はコピペで使えます。
ぜひ参考にして、活用してみてください。

1.コード

function getGroupListAll(){  
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sh = ss.getSheetByName('test');

  let output = ([["グループ名", "アドレス", "エイリアス"]]);
  let domainList = ["ドメインを入力","管理ドメインが複数ある場合は続けて入力"];
  //ドメインに@は不要

  domainList.forEach(x=> pushArray(output,getGroupInfo(x)));
  inputSpace(output);
  sh.clear();
  sh.getRange(1,1,output.length,output[0].length).setValues(output);
}


//グループ情報を取得
function getGroupInfo(domain){
  let maxResults = 200;  
  let token = '';
  let result = []
  do{
    if(token==""){
      var groupsList = AdminDirectory.Groups.list({domain: domain, maxResults: maxResults})
    }else{
      var groupsList = AdminDirectory.Groups.list({domain: domain, maxResults: maxResults, pageToken: token})
    }
    if(groupsList.groups) {
      for(let g of groupsList.groups){
        let groupName = g.name//グループ名
        let groupMail = g.email//メールアドレス
        
        let nonEditable = g.nonEditableAliases; //自動付与設定されているエイリアス
        let editable = g.aliases; //自動付与以外に個別設定したエイリアス
        let concatedAliase = concatAliase(nonEditable, editable); //結合

        let forPush = [groupName,groupMail];
        result.push(forPush.concat(concatedAliase));
        }
      }
      token = groupsList.nextPageToken
    }while(token)
  return result
}


//エイリアス配列を必要に応じて結合して返す
function concatAliase(nonEditable, editable){
  if(nonEditable && editable){
    return nonEditable.concat(editable);
  }else if(nonEditable && !editable){
    return nonEditable;
  }else if(!nonEditable && editable){
    return editable;
  }else{
    return [];
  }
}


//破壊的に二次元配列に二次元配列を結合する
function pushArray(array1, array2){
    for(let arr2 of array2){
      array1.push(arr2);
  }
}


//長さの違う2次元配列に空白を入力する
function inputSpace(array){
  let wide = 0;
  for(let arr of array){
    if(arr.length>wide){
      wide = arr.length;
    }
  }
  for(let arr of array){
    while(arr.length<wide){
      arr.push('');
    }
  }
}

 

2.出力結果

こんな感じでスプレッドシートに出力されます。

エイリアスリスト取得結果

それぞれのエイリアス設定件数に応じて横に伸びていく形になります。

3.解説

3-1.AdminDirectory

グループ情報取得処理でAdminDirectoryを使用します。
エディタ左側の「サービス」から「AdminSDK」を追加しないと使えません。

Groups.listの公式リファレンス

3-2.構成

以下のような流れになっています。

ヘッダ、ドメインリストを作成

グループ情報を取得する中でエイリアス情報も一緒に取得
└2種類のエイリアスを結合

ヘッダにグループ情報を結合

行ごとの長さが異なるジャグ配列なので、これを貼り付け可能な二次元配列に整形

シートクリアして貼り付け

3-3.ポイント

3-3-1.取得上限

Groups.listは1リクエスト当たりの上限があります。
200件が上限のため200件以上の場合はトークンを取得して
次ページ分の情報取得へ進んでいく必要があります。

コード内で.nextPageToken()してトークンを取得、
do while(token)でトークンがある場合は次のページを取得しています。

3-3-2.nonEditableAliaseとaliaseについて

エイリアスには2種類あります。
アカウント作成と同時に固定的に生成するエイリアスを設定している場合、
そのエイリアスはnonEditableAliaseとして扱われます。

尚、nonEditableAliaseもaliaseも0件の場合はundefinedが返ってきますので、
concatAliase()でundefinedを除外して結合するようにしています。

4.活用方法

オススメの活用方法は「日次トリガーで参照用マスタにする」です。
Admin画面からエイリアス設定を参照するのは少し手間がかかりますし、
Admin画面を参照できる権限者にしかできない事です。

毎日深夜に更新されるよう設定しておけば工数ゼロなので、是非どうぞ。

5.さいごに

エイリアス参照が楽になる方法をお伝えしてきました。

今回の方法は、エイリアスを大量に設定していない限りは
グループメンバー一覧化と異なり情報量が少ないと思うので
タイムアウトやセル数上限についても気にしなくて良いかと思います。

ぜひ気軽に導入してみてくださいね。

GASでGoogleグループのメンバー一覧を取得するコードと解説

0.はじめに

GoogleWorkspaceを管理されている方々なら
「Googleグループを一覧化したい、マスタ管理したい」
そう思う事が絶対にあると思います。

そんな方に向けて、グループメンバーを一覧化するコードをご紹介します。
簡単に解説もしていきますので、ぜひ参考にしてみてくださいね。

1.コード

サンプルコードです。


//メイン処理
function getGroupListAll(){  
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sh = ss.getSheetByName('test');

  let domainList = ["ドメインを入力","ドメインを入力"];
  let output = ([["グループ名", "メールアドレス", "メンバー数", "グループメンバー"]]);

  //outputに対して一気にグループ情報とメンバー情報を付加
  domainList.forEach(x=> pushArray(output,getGroupInfo(x)));

  //2次元配列で貼り付けるための整形
  inputSpace(output);

  //シートクリア
  sh.clear();

  //貼り付け
  sh.getRange(1,1,output.length,output[0].length).setValues(output);
}


//ドメインのグループを全件取得する
function getGroupInfo(domain){
  let maxResults = 200;  
  let token = '';
  let result = []
  do{
    if(token==""){
      var groupsList = AdminDirectory.Groups.list({domain: domain, maxResults: maxResults})
    }else{
      var groupsList = AdminDirectory.Groups.list({domain: domain, maxResults: maxResults, pageToken: token})
    }
    if(groupsList.groups) {
      for(let i in groupsList.groups){
        let groupName = groupsList.groups[i].name//グループ名
        let groupMail = groupsList.groups[i].email//メールアドレス
        let groupNumber = groupsList.groups[i].directMembersCount//メンバー数
        let groupMember = getMemb(groupsList.groups[i].email)
        let forPush = [groupName,groupMail,groupNumber]
        result.push(forPush.concat(groupMember))
        }
      }
      token = groupsList.nextPageToken
    }while(token)
  return result
}


//addressのメンバーを1次元配列で取得
function getMemb(address){
  let rv = [];
  let member = ''
  let token = '';

  do{
    if(token == ''){
      member = AdminDirectory.Members.list(address,{maxResults:200});
    }else{
      member = AdminDirectory.Members.list(address,{maxResults:200, pageToken: token});
    }
    if(member.members){
      member.members.forEach(x => rv.push(x.email));
    }
  token = member.nextPageToken;
  }while(token)

  return rv;
}


//破壊的に二次元配列に二次元配列を結合する
function pushArray(array1, array2){
  for(let arr2 of array2){
    array1.push(arr2);
  }
}


//長さの違う2次元配列に空白を入力する
function inputSpace(array){
  let wide = 0;
  for(let arr of array){
    if(arr.length>wide){
      wide = arr.length;
    }
  }
  for(let arr of array){
    while(arr.length<wide){
      arr.push('');
    }
  }
}

多分もっと分かりやすく、綺麗に書く事もできると思います。
ちなみにAIチャットボットChatGPTに書いてもらうとこんな感じになります。
AIチャットボットにGASを書いてもらったら実用レベルで絶望した件

2.出力結果

こんな感じにスプレッドシートに出力されます。
メンバーリストの出力結果画像

3.解説

3-1.AdminDirectory

グループ情報取得、メンバー情報取得の両方の処理でAdminDirectoryを使用します。
エディタ左側の「サービス」から「AdminSDK」を追加しないと使えません。

Groups.listの公式リファレンス
Members.listの公式リファレンス

3-2.構成

大きな流れとしては、こんな感じになっています。

貼り付け用の二次元配列とヘッダを用意

各ドメインをループし全グループを取得
└ループ内で各グループのメンバーを取得

行ごとに長さの違うジャグ配列になっているため、
列数を揃えるためにinputSpace()で整形

シートをクリアして貼り付け

3-3.ポイント

Groups.list、Members.list両方とも1リクエスト当たりの上限があります。
200件が上限のため200件以上の場合はトークンを取得して
次ページ分の情報取得へ進んでいく必要があります。

コード内で.nextPageToken()してトークンを取得、
do while(token)でトークンがある場合は次のページを取得しています。

4.注意点

4-1.セル数上限に注意

「3000人入っているグループが5000件ある・・・」等、
ボリュームがある場合はスプレッドシートのセル数上限に注意してください。

セル数上限は昨年引き上げられてスプレッドシート当たり1000万になりましたが、
人数分、右方向にセルを使う仕様のため要注意です。

対策したい場合、1行100列までにして次の行に折り返す工夫などが必要です。

4-2.タイムアウトに注意

グループを全件取得し、更にメンバーを全件取得しています。
グループ2000×メンバー3000だと単純に掛け算して6,000,000。
処理数が多くなるので時間がかかります。

対策としては、ループをラムダ式に置き換え高速化を図るか、
タイムアウトしそうになったら一度処理を終了して
続きから実行するトリガーを設定するような手法が必要になります。

何にせよ、一度ご自身の環境で実行して対策の要否を検討してみてください。
私の環境では2000グループ×2000名ほどですが、何とかなっています。

5.さいごに

いかがでしたでしょうか。
これがあると、Admin画面を見られない方にも参照して頂けます。
メンバーをユーザー名で表示したい場合、
こちらの記事も参照してユーザーリストと照合する仕組みにしてください。
GASで全ユーザーのリストをスプレッドシートに日次取得する方法
ぜひ活用してみてくださいね。

GASでGoogleドキュメントを効率化!書き込む時の重要操作方法一覧!

0.はじめに

GAS活用ではシートやドライブ、フォームにスポットが当たりがちですが
Googleドキュメントも扱えます。

基本的な書き込みや改ページ、書式操作などをご紹介します。
また活用事例も簡単にご紹介します。

1.最初にbodyを取得

必ず最初にこの手順を行ってください。

基本的にここで取得したbodyに対して変更を重ねることになります。
また上書き保存や改ページはdocに対して実行します。

const doc = DocumentApp.openById(docid);
let body = doc.getBody();

2.ドキュメント内容を削除する

1つのドキュメントを繰り返しリセットして編集する場合、処理の頭で実行しておきましょう。

body.clear();

3.末尾に行を追加

ドキュメント末尾にテキストを追加します。

body.appendParagraph("追記テキスト");

4.右揃えや中央揃えを設定する

//右揃え
body.appendParagraph("追記テキスト").setAlignment(DocumentApp.HorizontalAlignment.RIGHT);
//左揃え
body.appendParagraph("追記テキスト").setAlignment(DocumentApp.HorizontalAlignment.LEFT);

//中央揃え
body.appendParagraph("追記テキスト").setAlignment(DocumentApp.HorizontalAlignment.CENTER);

5.文字サイズを設定する

body.appendParagraph("追記テキスト").editAsText().setFontSize('14');

6.太字・下線を設定する

//太字
body.appendParagraph("追記テキスト").editAsText().setBold(true);
//下線
body.appendParagraph("追記テキスト").editAsText().setUnderline(true);

7.文字色・背景色を設定する

他のメソッドと違って引数が複数あるので注意です。
開始位置、終了位置、色コードを設定します。

//文字色 1文字目~3文字目を黄色に
body.appendParagraph("text").editAsText().setForegroundColor(0,2,"#ffff00");

//背景色 2文字目~5文字目を赤背景に
body.appendParagraph("text").editAsText().setBackgroundColor(1,4,"#e60033");

8.ドキュメントに改ページを挿入する

項1で定義したドキュメントオブジェクトに対して実行します。

body.appendPageBreak();

9.ドキュメントを保存して閉じる

編集が終わった後に保存する必要がある場合、実行してください。

doc.saveAndClose()

10.メソッドチェーン

「太字にしてサイズ変えて右揃えにして・・・」を一気に書けます。

const text = "テストですよ";
body.appendParagraph(text)
         .setAlignment(DocumentApp.HorizontalAlignment.RIGHT)
         .editAsText().setFontSize('18')
         .editAsText().setForegroundColor(0,text.length-1,"#e60033")
         .editAsText().setBackgroundColor(0,text.length-1,"#ffff00")
         .editAsText().setBold(true)
         .editAsText().setUnderline(true);

11.番外:PDF保存

作成したドキュメントをPDFとしてドライブに保存します。
スプレッドシートをシート指定してPDF化するより安定します。

//ドキュメントをPDF形式でエクスポートするURL
const url = `https://docs.google.com/document/d/${ドキュメントのID}/export?format=pdf`;

//Access Tokenを取得
const token = ScriptApp.getOAuthToken();
    
//PDF形式でドライブに保存
const res = UrlFetchApp.fetch(url, {headers: {'Authorization': 'Bearer ' +  token}}).getBlob().setName("保存PDF名称.pdf");
const folder = DriveApp.getFolderById("保存先フォルダID");
const pdfFile = folder.createFile(res);

12.活用例

  • 安定したPDF作成の仕組みを作る(SS⇒PDF作成より安定する)
  • 定型フォーマットのDocを作成するGASを作り、可変部分は変数にしておき
    その時々で必要な値を入力した定型のドキュメントをワンクリックで作成可能にする

私も実務で1つ目のパターンを活用しています。
スプレッドシートからシート指定でPDF作成すると本当に安定しないのでこれで代用しています。

13.さいごに

ドキュメント作成に関するGoogle公式リファレンスはこちらです。
もっと細かく設定したい方は、公式情報も参照しつつ色々試してみてください!

GASでAsanaAPIを使ってタスクを自動的に作成するためのコード

0.はじめに

タスク管理ツールAsanaに対して
GASからAPIを叩いてタスク作成する方法をご紹介します。

1.コードサンプル

まずコードをどうぞ。

/**
 * asanaAPIでタスク登録
 */
function test(){
  //個人アクセストークンの取得方法はこちらhttps://asana.com/ja/guide/help/api/api
  const headers = {
    "Authorization": "Bearer " + "実行するアカウントの個人アクセストークン"
  };

  //Asanaに登録する基本的な内容 他の項目や詳細はこちらのcreate-a-task https://developers.asana.com/docs/create-a-task
  const payload = {
    "data":{
        "workspace"  : "ワークスペースのID",             //ワークスペースID
        "projects"   : ["プロジェクトID"],               //プロジェクトのURLにあります
        "name"       : "title",                         //タスクタイトル
        "assignee"   : "担当者メールアドレス",            //タスク担当者メールアドレス
        "html_notes" : "body",                    //htmlで本文作成したい場合bodyタグで括る必要あり
        "due_on"     : "2023-01-23",                    //タスク期日 YYYY-MM-DD形式
        "followers"  : ["~~~~@ドメイン.jp"]            //コラボレーターアドレス 配列でいいはず 組織外アドレスは追加できなさそうです
    }
  };

  const options = {
      "method"              : "post",
      "contentType"         : "application/json",
      "headers"             : headers,
      "payload"             : JSON.stringify(payload),
      "muteHttpExceptions"  : true
  };

  //タスク生成
  const response = UrlFetchApp.fetch("https://app.asana.com/api/1.0/tasks", options);  
  
  //これで実行結果が取れます
  console.log(response.getContentText());

  //実行結果からタスクIDを取得、タスクURLを作成 このURLをSlackに飛ばすなり何なり
  const taskId  = JSON.parse(response).data.gid;
  const taskUrl  = "https://app.asana.com/0/" + "プロジェクトID" +  "/" + taskId;
}

2.事前取得すべき情報について

いくつか事前に情報を取得する必要があります。

2-1.AsanaワークスペースIDの取得

契約している会社ごとのIDの事です。コードの最初の方で使っていますね。
ご自身の会社のIDを取得する必要があります。

Asanaにログインして、その状態で下記のリンクに飛んでください。
https://app.asana.com/api/1.0/workspaces

そうするとシンプルなテキストだけのページが表示されます。
その中のgidがワークスペースIDになります。

2-2.アクセストークンの取得方法

こちらは下記のリンクを参照しながら値を取得してみてください。
アクセストークンの取得方法

2-3.リクエストやレスポンスについて

リクエストの細かい設定方法やレスポンス内容の公式リファレンスはこちらです。
create-a-taskに関する公式情報

3.注意すべき点

上記のリンクやコードを細かく見た方はお分かりになると思いますが、
htmlでタスクbodyを作成する時のルールや、
組織外のアドレスを追加できない仕様など細かい条件があります。

上手くいかない場合、その辺りの条件が正しいか公式情報を確認してみてください。

4.活用アイデア

Googleフォームで依頼を受けて自動的にタスク化する

Asanaというとプロジェクト管理等で使われれる印象です。
が、このサイトはバックオフィスがテーマなので、バックオフィス寄りの提案です。

Googleフォームの回答があった際にGASを自動発動できるので、
その設定を利用してGASでフォームの情報をAsanaに飛ばします。
フォームからは回答者のメールアドレスや回答内容を取得可能なので、
回答者をコラボレーターに追加し、自部署担当者をAsanaタスク担当者に設定しましょう。

依頼種別ごとに複数のGoogleフォームを作成し、
内容によってタスク担当者やコラボレーターが変わるようにすると更に活用範囲が広がります。

フォームの種類があまりにも多い場合、それぞれのフォーム内のGASに対して
担当者情報等をハードコードするのはオススメできません。
担当者が変更になる時のメンテナンスコストが非常に高くなるからです。
どのフォームに誰を紐づけるか管理するスプレッドシートを用意し、
全てのフォーム⇒AsanaのGASがそのシートを参照してからタスクを作成する。
そんな仕組みにすると良いでしょう。

5.おわりに

フォームからAsanaに飛ばしたり、シート情報を元に生成したり、
GASで作成したwebアプリからタスク生成したり、色々できそうですよね。

Saasツールはこういう機能をフル活用してこそ、効果が上がると感じます。
ご活用いただけると幸いです。

XeoryBaseで記事内アイキャッチ画像の横幅を全体に広げる方法【wordpress】

0.はじめに

当ブログの外観としてXeoryBaseというテーマを使用しています。
無料で優秀なテーマとして知られていますが、
カスタマイズを必要とするシーンも多々あります。

今回は、最近施したアイキャッチ画像のサイズ変更について取り上げます。
通常だと記事内の右側に縮小表示され、扱いにくい感じになってしまう問題の対処です。
ブログ趣旨とズレますが、誰かの役に立てるかなと思い投稿してみます。

1.手順1 – single.phpを編集

テーマファイルエディターを表示してください。

左メニューからテーマファイルエディタを開く

single.phpを開き、
the_post_thumbnail()の()内を‘full’に変更してください。

single.phpを編集する

これで、アイキャッチのサイズがフルサイズになります。

他のサイズにしたい場合、下記を参照して()内を変更してみてください。

‘thumbnail’ : 150px × 150px
‘medium’    : 300px × 300px
‘large’    : 640px × 640px
‘full’    : 画像の元サイズ
array(n,n)   : 任意のn×nのサイズ
参照:テンプレートタグ the post thumbnail

2.手順2 – style.cssを編集

手順1だけでも画像のサイズは大きくなりますが、
アイキャッチ画像の左端に微妙な隙間があります。
これを削除するには、style.cssを変更します。

style.cssの、.post-thumbnailのargin-leftの指定を/*と:/で囲ってください。
もしくは削除してもOKです。

cssで.post-thumbnailのmargin-leftを無効化する

これで左端の余白も無くなりました。2023年2月現在の当サイト各記事と同じ設定になります。
尚、アイキャッチの下部にも実は余白が設定されています。
これを削除したい場合、margin-bottomも上記と同様の方法で無効化してください。

3.さいごに

phpには馴染みがありませんが、普段からプログラムを書いているからか
思っていたよりスムーズに変更できました。
またやっている事の意味が分かるので、不安も少ないですね。
こういうところでも、プログラミングを学ぶことのメリットを感じます。

wordpress初心者の方も、プログラミング初心者の方も
GAS等で簡単なwebアプリを作成できるところまで理解していると世界が変わります。
webの構造が何となく分かっている状態って結構強いんですよね。
興味があれば、ぜひ学習してみてくださいね。

脱ハードコード!問い合わせ削減!GASで設定をシート上に持つ方法

0.はじめに

GASをある程度深めた方。
管理の質に着目した事はありますか?

例えばノンプログラマーに対して業務ツールを提供したとします。
自動メールのテンプレートやCC宛先を変更するために、毎回ユーザーから依頼が入ります。
これ、運用管理者側で変更してもらえると楽ですよね。

今回はその1つとして、スプレッドシート上で設定値を保持して
それらの情報を使用する際に取得する
ところまでご紹介します。

1.【事例1】メールテンプレート設定

1-1.スプレッドシート側

まず、スプレッドシート側の具体例です。
画像をご確認ください。
設定シート

1-2.GAS側

ではこの設定情報をどうやって取得するか、GASコードを見ていきます。

function myFunction(){
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sh = ss.getSheetByName("setting");
  const vals = sh.getRange("A2:H").getValues().filter(x => x[0]); //A列が空でないvaluesにfilter
  const settingObj = vals.map(x => new MailSetting(x));
  console.log(settingObj);
  /**
   * settingObjの中身
  [ 
    { Id: '01',
      Subject: '【test1】住所変更申請を受け付けました【%日時%】',
      Body: '長いので省略しますが、本文列の内容が入ってます。',
      To: [ '%申請者%' ],
      Cc: [ 'jusho_sys@test.test.jp', 'jibunnoemail@jibunnoemail.com' ],
      Bcc: [],
      FromName: '住所申請システム',
      FromAddress: 'jusho_sys@test.test.jp' },
    { Id: '02',
      Subject: '【test1】緊急連絡先変更申請を受け付けました【%日時%】',
      Body: '長いので省略しますが、本文列の内容が入ってます。',
      To: [ '%申請者%' ],
      Cc: [ 'kinkyu_sys@test.test.jp', 'jibunnoemail@jibunnoemail.com' ],
      Bcc: [],
      FromName: '緊急連絡先申請システム',
      FromAddress: 'kinkyu_sys@test.test.jp' } ]
   */

  //settingObjの中身から使う時は
  const setting02 = settingObj.find(x => x.Id == "02");

  //body等の%申請者%や%日次%はreplaceを使って置換します。
  const body = setting02.Body.replace("%申請者%","山中田村丸太郎乃介");
}

class MailSetting{
  constructor(rowArray){
    this.Id          = rowArray[0];
    this.Subject     = rowArray[1];
    this.Body        = rowArray[2];
    this.To          = rowArray[3].split(",").filter(x => x);
    this.Cc          = rowArray[4].split(",").filter(x => x);
    this.Bcc         = rowArray[5].split(",").filter(x => x);
    this.FromName    = rowArray[6];
    this.FromAddress = rowArray[7];
  }
}

お分かりいただけたでしょうか。
クラスとmapを活用して一気にobject配列を作成しています。

Googleフォームに対して自動返信をするとした時、
全てのGoogleフォームがこのスプレッドシートを見て自動返信してくれたとしたら、
非常に管理が楽になりますよね。

コードを直接触る必要も無くなり、メンテミスの可能性が下がります。
本文を編集するには若干htmlの知識が必要ですが、
それでも大部分は運用管理者が実務に合わせてメンテナンスできると思います。
「システム担当にわざわざ頼むの面倒・・・」という事もなくなりますね。

2.【事例2】発動日を指定したい

2-1.スプレッドシート側

超シンプルです。こんなのもアリです。

設定シート

2-2.GAS側

function myFunction2(){
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sh = ss.getSheetByName("setting2");
  const targetDates = sh.getRange("B2").getValue().split(',').filter(x => x);

  //day.jsを使います 左のライブラリに追加してください
  //スクリプトID 1ShsRhHc8tgPy5wGOzUvgEhOedJUQD53m-gd8lG2MOgs-dXC_aCZn9lFB
  const today = dayjs.dayjs();
  //毎日
  if(targetDates.find(x => x == today.date())){
    //毎日トリガーで実行した上で、設定シート上の日付と一致する場合だけメイン処理を実行する、等
  }
}

コード欄にもコメントで説明を書いていますが、
毎日起動するトリガー設定をした上で、設定シート上の日付の場合だけ処理を行うようなコードです。

ライブラリday.jsを使用しているので、コピペで使う場合はライブラリに追加してください。
追加に必要なIDは「1ShsRhHc8tgPy5wGOzUvgEhOedJUQD53m-gd8lG2MOgs-dXC_aCZn9lFB」です。

3.おわりに

いかがでしたでしょうか。

GASは当然Googleサービスとの繋がりが強いので、
設定を外部化するための1つの方法としてスプレッドシート活用が有効な候補になります。
小規模開発でもメンテナンス性を上げるために、活用しない手はないでしょう。

また今回の記事で活用したmapやfindについてこちらの記事で解説しています。
GAS初心者がfor文やif文の次に学ぶと急激にレベルアップする技術7選
興味のある方は併せてご覧ください。

それではまた。ありがとうございました。

GAS初心者がfor文やif文の次に学ぶと急激にレベルアップする技術7選

0.はじめに

GAS初心者の方々、こんな悩みはありませんでしょうか。

「次に何を学べばいいのか分からない」
「とりあえずforとifで色々できるようになったけど、ネストだらけ・・・」

そんな方々へ、次の1歩として覚えると非常に便利になり
またコードを書くのがずっと簡単に、楽しくなる技術を紹介します。

1.for in

まずはfor inです。先にサンプルをどうぞ。

function myFunction() {
  const test = [0,1,2,3,4,5,6,7,8];

  //これをループする時、一番最初に学ぶfor文ではこう書きます。
  for(let i = 0; i<test.length; i++){
    console.log(test[i]);
  }

  //for inを使うと、こうなります。
  for(let i in test){
    console.log(test[i]);
  }
}

MDNリファレンス for-in

はい、短くなりましたね!
配列をループする時、配列の最初から最後までのインデックスを加算しつつループしてくれます。
長ったらしいfor文を書くよりスッキリして楽に書けてGoodです。

2.for of

続いてfor ofです。私も非常によく使います。


function myFunction() {
  const items = [0,1,2,3,4,5,6,7,8];

  //これをループする時、一番最初に学ぶfor文ではこう書きます。
  for(let i = 0; i<items.length; i++){
    console.log(items[i]);
  }

  //for ofを使うと、こうなります。
  for(let item of items){
    console.log(item);
  }
}

MDNリファレンス – for of

はい。お分かり頂けましたでしょうか?
for of文は、インデックスナンバーをループするわけではなく
配列内の中身を順番に取り出してくれます。

for inとの使い分けですが、

処理中にインデックスナンバーが必要な場合はfor in、
配列の中身だけが必要な場合はfor of

という風に考えるとよいでしょう。

実際にコピペして動かしてみると実感がわきますよ。

3.連想配列

これはプログラミングを深めるに当たっては避けて通れない技術です。
次項からの内容と絡めると大いに役立つので、ぜひ理解してください。

function myFunction() {
  let player = {name:"田中", age:20, team:"腹八分目"};

  console.log(player.name); //田中が出力される
  console.log(player.team); //腹八分目が出力される

  //チームを移籍したら・・・これで変更できる。
  player.team = "満腹党";
  console.log(player.team); //満腹党が出力される
}

これだけだと「何のためにあるの?」と思うかもしれません。すいません。

playerについて氏名や年齢の他にも、所属履歴や家族構成、緊急連絡先、身体測定の結果などを管理したい場合、
普通の配列だと「index12番目は家族構成で・・・」と、かなり運用に難が出ます。
そんな時、連想配列を使うと簡単に目的のデータに辿り着く事ができます。

4.JSON形式

ジェイソンと呼びます。嘘じゃないです。
何となく専門的な雰囲気がありますが、配列みたいなもんです。難しく考えないでください。


function myFunction() {
  let players = [
                  {name:"田中", age:25, team:"腹八分目", mail:"tanaka@harahachi_test.co.jp"},
                  {name:"田村", age:20, team:"腹八分目", mail:"tamura@harahachi_test.co.jp"},
                  {name:"田村中", age:23, team:"満腹党", mail:"tamuranaka@manpuku_test.co.jp"}
                ];

  //普通の配列と同じようにpushできる。
  players.push({name:"田井中", age:26, team:"満腹党", mail:"tainaka@manpuku_test.co.jp"});

  for(let player of players){
    console.log(player.mail);
    //1人ずつ順番にメールを送る処理などができる。
  }
}

前項では1名分しか管理しなかった内容について複数のデータをplayersという1つの変数で管理できます。
また配列にいくつかのデータが入っているようなものなので、ループも回せます。

例えばスプレッドシートに管理している数十人のデータを取得してJSON形式にしたら、
この例のように実務でも扱いやすそうなイメージが湧いてきませんか?

5.filter

ここからが本番です!
filterとの文字通り、配列の中身をフィルタリングします。

function myFunction(){
  //例1
  const numbers = [0,55,23,45,899,6443,23,456,47];
  const under100 = numbers.filter(num => num<100);
  console.log(under100);
  //[ 0, 55, 23, 45, 23, 47 ]が出力される

  //例2
  const players = [
                  {name:"田中", age:25, team:"腹八分目", mail:"tanaka@harahachi_test.co.jp"},
                  {name:"田村", age:20, team:"腹八分目", mail:"tamura@harahachi_test.co.jp"},
                  {name:"田村中", age:23, team:"満腹党", mail:"tamuranaka@manpuku_test.co.jp"},
                  {name:"田井中", age:26, team:"満腹党", mail:"tainaka@manpuku_test.co.jp"}
                ];
  const team8 = players.filter(player => player.team == "腹八分目");
  console.log(team8);
  /**
  [ { name: '田中', age: 25, team: '腹八分目', mail: 'tanaka@harahachi_test.co.jp' },
    { name: '田村', age: 20, team: '腹八分目', mail: 'tamura@harahachi_test.co.jp' } ]
  が出力される。
   */

  //その後の活用例
  for(let player of team8){
    let mail = player.mail;
    //teamが腹八分目のメンバーだけにメールを送ったりできる!
  }
}

MDNリファレンス – filter

イメージ頂きやすいように書きましたが、サンプルを見るだけで理解できたら凄いです。

例1は、数字配列を100未満のものだけにフィルタしています。
filter(num => num<100)の部分ですが、numの中に配列の中身が順番に入って
100未満かどうかを確かめている、という風に理解してください。
for ofに100未満という条件が付いた感じに近いかもしれません。

例2ではJSON形式に対して実行してみました。これも業務で非常によく使うパターンです。
もし練習したい場合、25歳以上の人だけを取り出してconsole.logで確認してみてください!

6.find

filterを理解できた方、素晴らしいです。
findも非常に似ていますので、似た感覚で見てください。

function myFunction(){
  const players = [
                  {name:"田中", age:25, team:"腹八分目", mail:"tanaka@harahachi_test.co.jp"},
                  {name:"田村", age:20, team:"腹八分目", mail:"tamura@harahachi_test.co.jp"},
                  {name:"田村中", age:20, team:"満腹党", mail:"tamuranaka@manpuku_test.co.jp"},
                  {name:"田井中", age:26, team:"満腹党", mail:"tainaka@manpuku_test.co.jp"}
                ];
  const team8 = players.find(player => player.age == 20);
  console.log(team8);
  //  {name:"田村", age:20, team:"腹八分目", mail:"tamura@harahachi_test.co.jp"}が出力される。
}

MDNリファレンス – find

age==20のメンバーは2人いるのに1人しか出力されない点に注目してください。
filterでは、条件に一致した時点でそれを返す、という事が行われます。
ですのでfilterと違って、1つしか値が返ってきません。

これだけだと「filterでよくない?何に使うの?」と思う方も多いかもしれません。
個人的には、次の次の項で紹介する複合的な用途でかなり使います。

7.map

filter,findと似た文法でも処理内容は全く異なる超便利メソッド、mapです。
配列の中身を一定の内容に応じて操作し別の変数に格納します。


function myFunction(){
  //例1
  const numbers = [1,2,3,4,5,6,7,8,9];
  const numbers3 = numbers.map(x => x*3);
  console.log(numbers3);
  //	[ 3, 6, 9, 12, 15, 18, 21, 24, 27 ] が出力される

  
  //例2
  const players = [
                  {name:"田中", age:25, team:"腹八分目", mail:"tanaka@harahachi_test.co.jp"},
                  {name:"田村", age:20, team:"腹八分目", mail:"tamura@harahachi_test.co.jp"},
                  {name:"田村中", age:20, team:"満腹党", mail:"tamuranaka@manpuku_test.co.jp"},
                  {name:"田井中", age:26, team:"満腹党", mail:"tainaka@manpuku_test.co.jp"}
                ];

  //名前だけの配列を作りたい場合
  const names = players.map(x => x.name);
  console.log(names);
  //	[ '田中', '田村', '田村中', '田井中' ] が出力される

}

MDNリファレンス – map

例1では配列の各要素に対して3倍した配列が生成されています。
例2ではname属性の中身だけの配列が生成されています。

配列の各要素に対して処理を行った結果を配列として返す、という技術になります。

練習したい場合、
例1ではnumbersの各要素に対して10足した配列を作成、
例2ではplayersのmailだけの配列を作成してみてください。

8.複合技

集大成です。今までの内容を複雑に組み合わせた例を作ってみました。
少々理解に手間取るかもしれません。私自身も最初は脳ミソ爆発しそうでした。


function myFunction(){
  //スプレッドシート1から取得したプレイヤー情報
  const players = [
                  {id:0001 ,name:"田中", age:25},
                  {id:0002, name:"田村", age:20},
                  {id:0003, name:"田村中", age:20},
                  {id:0004, name:"田井中", age:26},
                  {id:0123, name:"田牧", age:32},
                  {id:0222, name:"田中", age:26},
                  {id:0333, name:"田坂", age:16}
                ];

  //スプレッドシート2から取得したチーム1のメンバーIDリスト
  const team1Master = [0003,0005,0222];

  //上記の2つを用いて、playersを元にチーム1のメンバー配列を作りたい
  const team1Players = players.filter(x => team1Master.find(y => y == x.id));
  
  console.log(team1Players);
  /**
  [ { id: 3, name: '田村中', age: 20 },
    { id: 146, name: '田中', age: 26 } ]
  が出力される。
   */
}

あちこちにデータがありマスタ情報と紐づけて必要な情報を生成する際にかなり使います。
業務上、散らかったデータを集めてどうにかするシーンって結構ありますよね。

今回の例ではplayersの1人ずつについて順番に検証し、
team1Masterの中でIDを検索して見つかった人、のみにフィルタした形です。

9.注意点

for文やif文だけでも書こうと思えば書けますが、これらの技術を使うと楽に書けます。
ただ、1つだけ注意点もあります。

8で紹介した複合技ですが、やり過ぎると非常に分かりにくいコードになります。
業務で実際に一度、filterやfind、mapを連結させて50行以上にしてしまった事があります。

確かにそれでも一気に書く事ができますし、処理途中で使用する変数の数は減ります。
しかし、不具合が起きた時に何がおかしいのか検出するのが困難になります。

なぜならfor文の中にはconsole.logを書けますが、filterの中にconsole.logは書けません。
また途中で変数に一度代入したりステップを踏むことで、不具合発生の段階を切り分け易くなります。

ですので「これはやり過ぎだな」と思ったら、途中で処理を区切ったり
別の関数に分けることを考えてみてください。

10.さいごに

いかがでしたでしょうか。難しかったでしょうか。
難しくても、苦しんで突破するだけの価値のある技術群です。

私も長い間、for文やif文だけでGASを業務活用していましたが、
やはりその頃のコードを見ると非常にムダが多いなと感じます。
ただfilterやmap、特に複合技は最初まったく意味不明で大変でしたが・・・。

また、forやifを多用する書き方から一歩先に進む方法も全然分かっていなかったので、
これらの技術を知った時に苦しみながらも、光明が見えたような感覚もありました。

これらの技術は確実にレベルを上げますし、
GASを発展させwebアプリ化する際のJavaScriptでも非常によく使います。
ぜひ習得してみてくださいね。

【約50件】総務系の改善事例/改善案を多角的にご紹介します【数年分】

0.はじめに

総務系のみなさま、こんにちは。
今回の記事では、私が今まで10年弱に渡り蓄えてきた
総務系改善の知識と経験を大放出します。
改善が進んでいない会社に適用していったら、数年はかかるボリュームです。
(私についてはこちらから。)

以下5つのカテゴリごとに解説していきます。

  • 経費削減
  • 業務効率化・改善
  • 電子化
  • 福利厚生・社内設備
  • その他

「業務改善ネタがほしい」「次の評価どうしようかな…」という方、
ぜひ自社に当てはめて想像しつつ、参考にしてみてください。

1.経費削減

電力会社の変更
大規模かつ継続的な削減効果があります。
1000名規模の工場系の会社で年間約900万円の削減を行ったことがあります。
シンプルに試算して契約を切り替えるだけですが、
「設備変更」や「移行期間」の有無は注視してください。
移行期間の調整を失敗すると電気が来ないので、シャレになりません。
電力系の会社にいくつか連絡してみると、積極的に現費用との比較試算を出してくれます。
社用車ETCカードの変更
こちらも車両台数次第ですが、大規模かつ継続的な削減になります。
100台規模で年間300~500万円程度の削減になったかと記憶しています。
こちらは契約してから発行までに時間を要する場合がありますので、
現契約との移行をスムーズに行うために必ず移行計画を立てましょう。
電力と同様、いくつかの発行元に連絡して試算してもらいましょう。
社用携帯の契約変更
固定電話契約の見直し
社用携帯の台数が多い場合は大きな効果が出ます。
端末料金と月額料金とも、ボリュームディスカウントをしてもらいましょう。
また社用ですから不必要に高スペックな端末も不要なはずで、スペックを落としましょう。
固定電話についても、不要な回線がないか今一度見直しましょう。
IP電話やクラウドPBXなど電話の方式によってもコストが変わってきます。
尚、固定電話については営業日以外で移行するなどの注意が必要です。
賃料削減
こちらも規模感が大きく、都心ビルだと月次一千万単位の削減すら可能な場合があります。
ですが、相手側として値引きに応じるメリットが少ない場合も多く、
その分対応が長期化して工数を取られる傾向も強いです。精神的負荷もあります。
大人しく経費削減コンサルに相談するのがよい分野だと個人的には思います。
リース物件の費用交渉
複合機、プリンタのカウント料金交渉
これらはコツコツ聞いてくるパターンですね。
リース契約は月次のリース料、プリンタはカウント料金=印刷1枚あたりの使用料金がターゲットです。
契約期間は様々ですが、車両や複合機や厨房機器のリースだと初度契約で5年、
産業機械などでは10年という物もあります。
一度変更してしまえば長期的に効果が継続する点が非常に良いですね。
これについては相見積もりを取って交渉するのが一般的でしょう。
「弊社の契約を全て御社に回したら月額いくらになりますか?」という風に
顧客を取りたい新規業者さんに見積を依頼して、比較してみてください。
また、必要以上に印刷スピードの速い複合機はスペックを落としましょう。
出張旅費の削減
緊急で決まった海外出張の場合、エリアと時期によってはハイグレードな席しか取れず
往復費用が100万円を超えてくる場合もあります。
1社メインで取引する旅行会社の他に、2~3社ほど専属になりたい旅行会社も開拓しておき
いざと言う時に個人旅行やツアープランのキャンセル分を拾ってもらえるような体制があると
「チケット取れない」というリスクヘッジと経費削減を同時に実現する事ができます。
ここに関わる業務を行うと、日々コツコツと数千円~数万円の削減を実施できますが、
1社専属の工数的メリットも大きいため、社内で相談しましょう。
また、社内の出張旅費規程がガバガバの場合、そこに手を入れるのも良いでしょう。
廃棄物契約の変更
産廃の排出がある場合、地味に効果が出ます。
「プラは1kgあたり60円、木材は再資源化できるから無料、一般廃棄物は月額」など、
品目や業者によってコストの形態が異なります。
基本的には重量当たりの処理費用を相見積もりで交渉するのが良いでしょう。
ただし地域によっては、人付き合いや関係性が重視される業界特性もあるため、
処分業者、運搬業者、自社の3当事者の関係性に傷を付けない慎重さが必要です。
また処分業者が悪さをすると排出事業者も責任を問われるため、
選定の際には廃掃法を今一度確認して処分場の見学を行いましょう。
見学、面白いですよ。
ゴミ分別方法の変更
前項が長いので分けました。
もし社内での分別が本来行うべき分別と異なっていた場合、
処分単価が高額な品目に誤って分別してしまっている可能性があります。
社内の分別を正して社会的にもコスト的にも良い事をできる場合があります。
実際に「布」や「混合物」の分別を正して、年間約10万円の削減を行った事があります。
日常消耗品の購入先変更
様々なところから購入できるので迷いどころではありますが、
企業を相手に大量の仕入れをして安く提供する事務用品屋が世の中にはあります。
インターネットでの購入でもかなり安くなる場合がありますが、
1社繋がりを持っておくと特定品目でコツコツ削減出来る場合があります。
また、いざ物が無くて困った時にも緊急で助けてもらえる場合があります。
非常用備蓄品の変更
こちらについては数量次第です。
かなり手厚く考えてくれる会社の場合、数量が大きくなるため
ネット等で購入せず業者に直接依頼する方が安上がりに済む場合があります。
逆にここの費用を絞る会社や、人数が少ない企業の場合、
拠点数が多く1拠点辺りの数量が少ない場合、Amazon等で買った方が安くなる傾向にあります。
また購入する物品についても細かく選定する事で、経費削減が図れるでしょう。
電灯のLED化
もう古いのが付いている会社は無いでしょうか?
これについては今の拠点を出るまでの期間で試算をしてみてください。
本体費用に加えて、交換頻度や交換工数も試算に含めましょう。
ちなみに非LEDはあと数年で流通が無くなる見込みですので、
本体価格や入手難易度も上がっていきます。
環境配慮など様々な面でも交換へのアピールポイントがありますから、
加えて結果的に削減できたコストも評価ポイントとしてアピールしていきましょう。
日々の値引き交渉
当たり前ですがこれです。
そして、色々な担当者を見ていて、よ~~~く思う事があります。
「ちゃんと削減額を算出して、都度記録してください。」
ここ、ないがしろにする人がめちゃくちゃ多いんですが、やりましょう。
「評価のために、がめついなあ」と思う方も居られるかもしれません。が。
「会社のコストの事も常に視野に入れながらコストセンターになりがちな自覚を持って仕事をする」
という優秀なバックオフィス社員に必要な行動だと思います。意識が勝手に身に沁みつきます。
評価されるかは会社によるとは思いますが、やって損になる事は無いと思います。

2.業務効率化・改善

物品整理/清掃
「何だそんなことか」と思うなかれ。
物品の置き場を全てマッピングして、全員分の探す時間を削減したらどうでしょうか。
書籍貸出制度があるとして、在庫を全てリスト化し電子で貸出対応可能にしたらどうでしょうか。
清掃業者を入れて清潔にすることで従業員の健康が増進したら素晴らしいですよね。
かなり大きな改善で、多くの人に感謝される仕事です。
工数を使わせてもらえるよう、実施するメリットをアピールしましょう。
社内申請/依頼の電子化
丸々次項で解説します。
印刷工数や保管工数、探す工数、床面積≒賃料などでメリットが出ます。
定期集計の自動化
定期配信の自動化
毎月集計するもの、毎月配信するものなどについては自動化しましょう。
RPAやプログラム、タスクスケジューラを活用することで実現可能です。
私自身もこの辺りは大量に実装しており、自動化端末が毎日勝手に動いてくれています。
尚、実行が失敗した時に必ず検知できるようなエラー処理を仕込むことが重要です。
githubなどを使ってソース管理もできると更に良いと思います。
期間管理の自動化
リース満了の3ヶ月前に通知したい、入社/退職の日に対象者へ自動連絡したい。
期間や期日などに合わせてほぼ定型の対応が必要な場合、自動化しやすいです。
建物や什器などの契約更新時は価格交渉のチャンスですから、
知らぬ間に自動更新されぬようにキャッチできるように仕組みを組んでおきましょう。
人事データ等の自動日次取得
バックオフィスに所属していると、従業員情報を参照する機会は日常的にあると思います。
そんな時、毎回毎回人事管理システムにログインするのは面倒ですよね。
深夜帯等にRPA等を使って日次でExcelやスプレッドシートに自動で落とし込ませておくと、
関数等で加工もし易い形式のため様々なシーンで活用でき、後から地味に効いてきます。
社内配信のテンプレート化
社内掲示のテンプレート化
配信や掲示は毎回内容こそ違えど、テンプレート化しておくと良いでしょう。
毎回ゼロから作るよりは圧倒的に工数が削減されます。
また見る側としてもフォーマットが固定化されると分かりやすくなります。
RPA活用/その他社用ツール開発
既に各項目で触れていますが、これができるかどうかでかなり会社が変わります。
自分たちの会社独自の仕事を自動化することで、パッケージソフトでは実現できないような
圧倒的なパフォーマンスを出すことができます。
以前、月次で発生する数千件の手作業を自動化した事があります。毎月気が狂いそうでした。
実際に社内で動いている効率化プログラムが全部無くなったら・・・恐ろしいです。
冗談抜きで倍ぐらいの人材がバックオフィスに必要になると思います。

既にプログラマが社内に居るなら協力関係を構築すること自体も評価されるべきです。
自分でやるしか無い状況の場合、おそらく無数に効率化対象があるはずです。
学習は大変かもしれませんが、仕事で実践しながら身に着けていくと楽しいと思いますよ。

3.電子化

電子稟議の導入
保管性/検索性/運搬性/高速性・・・移行メリットは多分にあります。
が、このプロジェクトは難航するケースが多い印象です。
ワークフローシステムの初期費用は勿論のこと、仕様も多種多様です。
稟議と経費申請を連携可能なもの、経路設定が柔軟なもの、マスタ管理が楽なもの、
書式設定が簡単なもの・・・色々とありすぎて挫折しそうになります。

選定の際には「外部連携性」「経路管理」「書式管理」「従業員設定」「通知機能」辺りに特に着目し、
自社に必要な部分を持つツールを選定し「費用感」と照らし合わせると良いでしょう。
時には自社の業務方法を曲げる必要性もあるかもしれません。

電子印/電子契約サービスの導入
保管性/検索性/運搬性/高速性・・・電子稟議と同等のメリットがあります。
また契約満了日を管理・通知してくれるシステム等もあるため、
仕事の精度にも効果をもたらしてくれます。
ただし取引先の傾向によっては電子契約を受け入れてくれない場合もありますので、
自社取引先のカラーを考慮に入れて慎重に導入を決める必要があるでしょう。
社内規程管理システムの導入
保管性や参照性は勿論のこと、Word等で管理するよりも
フォーマット管理を気にしなくて良い点、変更履歴参照が容易な点などで優位性があります。
大量の機能を必要とする性質でも無いと思うので、社内にプログラマが居る場合は
購入せず内製化するのも一つの手だと思います。
勤怠システムの導入
集計作業や給与業務の効率にも直結します。
非常に操作性の良いシステムも多く、ユーザが楽になる場合もあるかと思います。
検討の際には、入社者情報の取込方法や、勤務実績の出力方法、各フォーマットなどを確認し、
自社の業務フローに対して問題が無いかしっかり確認してください。
運用者への立場を考慮できる開発者は意外と少なく、それがそのまま仕様になっている事が結構あります。
人事管理システムの導入
人の情報をマスタ管理できるシステムです。
COMPANYや人事奉行など「人事管理システム」と呼ばれるものです。
入社時に履歴書の情報や個人情報などを登録し、異動履歴や保有資格等も管理できます。
管理できる情報はシステムによって様々ですが、これ1つあれば履歴書等を引っ張り出す必要もなく
人の情報をエクセル等で管理している場合はこれ1つに集約できます。
ただし勤怠管理や給与管理などに大きな影響を及ぼすため、各種の業務フローとの調整を慎重に考慮しましょう。
社内申請/依頼の電子化
紙申請を電子化するのは勿論のこと、
ワード等をメールで提出するような業務についても改善できるかもしれません。
当サイトでも扱っていますが、Googleフォーム等にプログラムを仕込んで申請してもらう事で
word文書の細かなフォーマット修正などが不要になります。
対象が多くなってきた場合、プログラムをライブラリ化し管理シートを用意するなど
規模感に合った管理方法を工夫するとより良いと思います。
会議資料/議事録の電子化
web会議ツールの導入
これに関しては時代が時代なので、特に言及する事もないですね。
ガンガンやっていきましょう。
電話受付/受付タブレットの導入
来客受付担当者の人件費をカットできます。設備費用も大してかかりません。
対人受付の廃止に抵抗のある会社の場合、人件費の削減試算だけでは提案が通らない事もあると思います。
電話/タブレットでありながらも来訪者に対して失礼の無い具体的運用と共に提案すると良いでしょう。
タブレット受付ではチャットツール等に通知を飛ばしてくれる物もあります。
部門単位のチャンネルに通知するなどの方法を取り、見逃しても周囲の人がカバー可能な仕組みを作りましょう。

4.福利厚生・社内設備

社員食堂の導入/食堂業者の変更
かなり大変なイメージがありますよね。ですが全社を巻き込んだ面白い取組になる事もあります。
社員食堂の選定の際は複数の食堂業者を試食する事になると思います。
私の経験では、複数社を同日に集めて各部署代表者を巻き込んだ試食会を実施した事があります。
今思えば、これも福利厚生と言えそうですし、エンゲージメントにも繋がると思います。
ただ味だけではなく、従業員が支払う費用感もかなり重要になってきます。
なるべく負担増にならぬよう、場合によっては会社が一部負担するような形も検討してください。
業者によっては飲食費用は会社から支払、後から給与天引きとなるケースもあります。

大変なプロジェクトではありますが、従業員アンケート等を取ると
社員食堂を希望する声は非常に多く上がります。声を聞くところから始めるのも良いかもしれませんね。

弁当屋の導入
社員食堂の導入は場所や設備の問題で難しい、という場合も多いでしょう。
そんな場合でも、従業員規模によってはお弁当屋さんと連携できるケースがあります。
午前中に社員から注文を集めるスタイルや、販売員さんに社内に来てもらうスタイル、
アプリで注文するフードデリバリーの会社版のようなサービスもあります。
会社が費用を負担せずに導入可能な場合もありますが、これは業者さんによります。
注意点としては、バリエーションを増やすために多くの業者さんと提携すると
1社あたりの食数が少なくなり、値上げや撤退となるケースがあります。
トラブルにならぬよう、業者が増える場合等は正直に既存業者さんと情報共有すると上手に付き合えます。
また、食品の取扱いに関する申請手続等をきちんと行う会社なのかどうか、 過去に食中毒等の発生事例がないか、等もチェックすると、社会的リスクの観点で安心です。
社内カフェ/社内バーの導入
よく見る事例ですね。
仕事の合間のリフレッシュとしてコーヒーが嬉しい人は多く、ヘビーユーザーが付く印象です。
また業務後に社内バーでコミュニケーション促進を行う会社もありますね。
いずれの場合も、行う内容によっては衛生関連の資格や許可が必要になる場合があり、
事前にかなり入念に調べておく必要はあります。
しかし、実際に様々に経験し従業員の声を聞くと、それだけの価値はありそうです。
カフェ運営が難しくても、個人では買いにくいコーヒーメーカーを社内に検討することは容易です。
最近では、こういった社内施設を運営してくれる業者さんもあります。
ウォーターサーバー/給茶機の導入
外回りの社員や、ランチ時など、社内にあると嬉しいですよね。
飲料を汲んでいる際に他部署社員とのコミュニケーションが発生するような効果もあります。
いくつか契約形態があり、事前に料金形態や納品形態を比較できると良いでしょう。
ウォーターサーバーの場合、水道直結、タンク式、ボトル式があります。
本体買切、レンタル、ボトルで利益を出すパターン、辺りが主流です。
ボトルの場合は納品頻度や使用量によっては場所をかなり取るため注意が必要です。
給茶機の場合も概ね同様です。本体無料の場合でも大抵は粉末代やコップ代がかかります。
気付いたら思わぬ金額の消耗品費が積み重なる場合もあるため、
運用開始後も定期的に長期試算をしましょう。
リフレッシュスペース/フリースペースの導入
「休憩時間に1人で過ごしたい」「自席から離れて過ごしたい」
そんな従業員も結構多く居るものです。
休憩時間の質を上げることが結果として業務品質に繋がる場合もあります。
経営者の考え方次第ですが、通りそうな場合は提案だけでもしておくと数年後に実現するかもしれません。
作業ブース/電話ブースの導入
「周囲の声が電話に入る」「周囲がうるさく集中できない」等に応える事ができます。
電話ブースは高品質な物を導入すると1つ数十万円します。
消火面や放送面など防火上の配慮等も必要になる場合があるので少しハードルは高いかもしれません。
権限者の方に、実際に効果を体感してもらいましょう。特に営業品質面を実感してもらうと良いでしょう。
運用ルールや予約方法なども、検討段階である程度固めておいた方が良いでしょう。
また、簡易的な仕切りブースとも効果と費用感を比較してみると良いでしょう。
パウダールームの設置
パウダールームとまで行かなくても、女性トイレへの配慮はあると喜ばれます。
拠点設計の際にはぜひ検討してください。導入後、必ず社員から反応があるはずです。
トイレの個室を多めに作る
人数に対して合っていないと、休憩時間など行列ができてしまう場合があります。
これも拠点設計の際に、過去実績から検討しましょう。
トイレの混雑緩和
上記と同様ですが、仕組みを導入する事で解消している事例があります。
社員の秩序によって効果の程は異なりますが、トイレセンサー等のサービスがあります。
混雑状況や入室時間を取得しているという状況が抑止力になり、結果的に改善されるというものです。
しかしこればかりは試してみないと効果が分からない部分もありますので、
1フロアだけから始めてみる、等のミニマムスタートで実験してみてください。
尚、賃貸ビルの場合は勝手に設置すると怒られる場合があるため貸主に事前確認してくださいね。
エレベーターの混雑緩和
これも解消すると非常にありがたいですよね。
初級編としては、エレベータの設定で待機階を利用が多い階に変更してみましょう。
エレベータの業者さんにお願いすれば短時間で変更してくれます。
基本的に階段を使うルール等も世の中にはありますが、不便ですよね。
奇策としては「EV内の床に線を引く」等の事例があります。
思わず線の中に収まるように立つ、という効果があるようです。
コロナ渦の商業施設で見かけた仕組みとよく似ていますね。
コストがかかりにくい工夫から試してみてください。
社内動線の再設計
設備配置など、少しの事でも従業員のストレスが減ります。
私の場合、給茶機の向きを90度変更しただけで食堂の動線が大きく改善した事があります。
また、こうした小さな事を実績としてPRできると、この職種に必要な「気付き力」を主張できます。
ちょっとした事ですが、管理者の立場から見てもこういう事に気付ける人は本当に重宝します。
各種設備の日常メンテナンス
当たり前の事ですが、従業員の満足度に直結します。
電子レンジの庫内や上、ウォーターサーバーの口や受け皿、衛生的ですか?
また設備の機能低下についても積極的に修理の提案をしていきましょう。
細かく対応件数があればあるほど、値引き交渉実績を積むチャンスも増えます。
清掃サービスの導入
従業員が朝や夕方に清掃する、という会社も少なくないと思います。
それも頭の切り替えになって良いかもしれませんが、不満を持つ社員も居るかもしれません。
また清掃の質は担保できないため、社風次第では清掃業者に置き換えても良いでしょう。
オフィス清掃の業者を探すと、清掃のプロとして実施してくれる業者さんが出てきますが、
家事代行サービス等も検討範囲に入れるとより視野が広がります。
清掃のプロの品質は高いですが、コストがかかります。
日常清掃は家事代行サービスを使い、定期的にカーペットクリーニング等を清掃業者に依頼するなど
うまく使い分けることでコストを抑えて運用できます。
社用備品の充実
ペンや文房具や電池など、社内で使用する物品は会社支給で完結できた方が無難です。
社長の観点では「ちょっとした事」ですが、自己負担を良く思わない社員は多いです。
また、電源延長コードなど一時貸出できると利便や可能性が増進する物は導入しましょう。
PCモニタを複数台利用可能に
プログラムや設計図を扱う専門職の方は非常に喜びます。
また、マウスやキーボードも一定金額内で好みの物を使わせた方が、効率とエンゲージメントが上がります。
アンケートを取って、また対象部門の役職者を味方に付けて、アピールしましょう。
保養施設の導入
年会費等がかかるイメージですが、社会保険機関や加入団体が実はサービスを持っている場合もあります。
現加入団体に付帯サービスが無いかチェックしてみてください。
主に宿泊施設について、1泊あたり数千円の割引が受けられる印象です。
ただ社風や申込方法によっては利用者数が伸びない場合もあるため、
なるべく会社を通さずに申込できるような制度設計ができると双方気楽に運用できます。
割引系の福利厚生サービスの導入
保養施設に近いです。加入する事で社員が飲食店やレジャーの割引を受けられます。
リロクラブやベネフィットステーションなど、一度検索してみてください。
社員の年齢層によって向き不向きはあると思います。

5.その他

補助金
国の補助金を取ってきましょう。
過去にはITツールの購入補助金や、エコ関係、コロナ関係などがありました。
申請難易度は時によって異なりますが、購入補助系は比較的楽なイメージがあります。
自分から情報を探さないと誰も教えてくれないので、定期的に検索しましょう。
チャットツールの導入
社風次第ですが、最近ではOffice365やGoogleWorkspace等の契約に含まれる場合もあり
比較的容易に導入できるようになってきていますね。
メールより気軽に、即時的に連絡が取れる点が魅力です。
またナレッジ共有用のチャンネルや部活用のチャンネル等をルールに従って運用する会社もあります。
業務での自動通知をチャットツールに流す事例等もネットに大量に流れており、
業務改善への可能性も広く開かれています。
メンバー教育/鼓舞
正直ここに書く事なのか迷いましたが、一応。
雰囲気を改善したり、レベルを底上げする事も重要な組織改善です。
率先して動く姿勢を見せる、積極的に発言する姿勢を見せる、等もやれそうであればやっていきましょう。
大きなテーマなので、別記事で触れられそうであれば触れていきます。
改善プロジェクトをやる
後輩や同僚を巻き込みましょう。
「色々改善案は持ってるから、MTGして紹介するよ。大変な部分は全部自分がやるから一緒にやって2人の実績にしよう」
と誘ってください。何回もやってますが、一度も断られた試しがありません。
もちろん、リードする必要はあります。この記事を見ながらリードしてください。
改善の思考方法や背中を自然と見せる事ができ、評価にも直結します。良い事しかありません。

6.この記事の活用方法について

項目数が50もあるため大変かもしれませんが、
自社で未実施の項目があればぜひ検討材料にしてください。
「自社状況に当てはめると、どうなるだろう」とシミュレーションしたり、
また同僚に共有したり記事を見ながらブレインストーミングをしても面白い場になると思います。

7.さいごに

本稿では網羅的にご紹介することを目標に、
事例の概略と検討時に考慮すべき事を大まかに記載しました。

他にも改善の際に助けとなる心構えや周囲の巻き込み方、
評価への上手な繋げ方などポイントとなる部分は沢山あります。
それぞれが重要なテーマになりますので、また別の機会にご紹介できればと思います。

また、この中に無いような改善対応があればぜひ教えてください!
私自身も思いつき次第、この記事は随時更新しようと思います。

それではまた別の記事でもお会いできる事を楽しみにしています。
ありがとうございました。

サルでも分かる Amazon商品URL短縮ボタンを3分で作る方法

1.はじめに

AmazonのURL、長くないですか?
スプレッドシートにAmazonの長すぎるURLが張ってあったとき、
「短縮してよ~」と思いながら自分で全部直したりしませんか?

この記事ではURLを一瞬で作るボタンを自作します。
周りの人のブラウザにも仕込んで使い方を教るとあなたが快適になります。
誰でも簡単に作成できますので、お気軽にどうぞ。

2.やり方

2-1.ブラウザのブックマーク設定を開く

Chromeの場合、ブラウザ右上の・・・ボタンから
「ブックマーク>ブックマークマネージャ」を開きます。
ブラウザのブックマークマネージャを開く

2-2.ブックマークの新規追加を選択

ブックマークマネージャ内の・・・から
新規追加を選択します。
ブックマークの新規追加

2-3.プログラム文字列を登録

下記の文字列を入力して保存してください。
これで完成です!

javascript:(function(){
s = "https://amazon.co.jp/dp/" + location.href.match(/(?<=\/)([A-Z0-9]{10,})(?=\/|\?|$)/)[0];
location.href = s; })();

3.実行してみる

商品ページでブックマークボタンを押すと
Amazonの商品ページ上で、先ほど追加したブックマークを押すと・・・

URLが短縮されます
はい、URLが短縮されました!

ブックマークが常時表示されておらず常時表示したい方は、
ブラウザの表示設定を変更してくださいね。
ブックマークを常時表示する

4.プログラムの中身について

4-1.概略

今回行った事は、こういう事です。

ブックマーク押下時に、
予めブックマーク登録した「プログラム」を実行する。

 

そして、そのプログラムの中身の概要はこうです。

「javascript」というプログラミング言語で
現在開いているページのURLを読み込み
「正規表現」という方法を使ってURLを分割。
必要な部分だけを抽出し
URLバーにその部分を貼り付ける。

です。

4-2.少しだけ細かく解説

まず、押すとjavascriptという言語でプログラムを実行します。

javascript:(function(){

URLから商品固有のコードを抜き出し(正規表現)
※正規表現の説明は非常に長くなるので省略します。

location.href.match(/(?<=\/)([A-Z0-9]{10,})(?=\/|\?|$)/)[0]

固定URL(https~dp/)と結合し、sという箱に入れます。

s = "https://amazon.co.jp/dp/" + location.href.match(/(?<=\/)([A-Z0-9]{10,})(?=\/|\?|$)/)[0];/* Your code... */

現在のページ(location)をsの中身にします。

location.href = s;

5.活用方法

5-1.購買系実務で使ってもらう

Amazonを利用したり参照するケースが多い企業の場合、
社内ポータル等に方法を書いておいても良さそうです。

5-2.JavaScript教育の導入とする

今回の方法以外にも、ブックマークレットにJavaScriptを登録して動きを見せましょう。
「web上の要素取得」「ボタンを押す」等が分かりやすいと思います。
そしてそれらの動きを見せましょう。一発でJavaScriptの動きに実感が湧くと思います。

「今回はURL短縮したけど、実はこんなのもできる。
 例えばこれ、ボタンを押すプログラムが仕込んである。
 これをクリックすると・・・ほら。
 こういうプログラムが、画面上のボタンとかに全部仕込まれてるんだよ。
 web画面ってそういう風にできてるんだ。」

という風な具合でしょうか。
結構お手軽で、学習前の人の興味を引くにはいいと思うんですけどね。

6.さいごに

はい。いかがでしたでしょうか。

私の場合、仕事でAmazonをよく使っていた時期がありました。
URLを周囲の方々へ送る時、申請書に記載する時、
スプレッドシートにまとめる時などによく活用していましたね。
簡単なので、周囲メンバーのPCにも設定して回っていました。

ぜひ活用して、広めてみてください。
少しでもお役に立てれば幸いです。ありがとうございました。