kintone
投稿日:

「工事日報、現場から戻ってから書いていませんか?」── kintoneで建設業の日報をスマホ入力化する

皆さんこんにちは。kintoneアプリエンジニアのtomiokaです。

先日、中規模の建設会社のお客様から「工事日報の管理が追いつかない」と相談がありました。

現場監督15名、常時20〜30件の工事を並行して進めている会社です。日報は現場から事務所に戻ってから紙に記入し、翌日に事務担当がExcelに転記するという流れで運用していました。写真は別途デジカメで撮影してフォルダに保存するのですが、どの写真がどの日報のものなのかが分からなくなる、、、、という状態が慢性化していたそうです。

今回は、現場からスマホで日報を入力し、写真添付と工数集計をkintoneで一元管理した事例を紹介します。現場監督10〜50名規模の建設会社で、日報を紙やExcelで管理している工事部門の管理者・事務担当の方に参考になる内容です。

紙・Excelでの日報管理で起きていた問題

建設業の工事日報は、工事の進捗記録だけでなく、労務管理や原価管理の基礎データにもなる重要な書類です。しかし紙やExcelで管理していると、以下の問題が発生します。

  • 現場から事務所に戻ってから記入するため、当日の作業内容を正確に思い出せない
  • 紙の日報を事務担当がExcelに転記する二重作業が発生している
  • 写真と日報が紐づいておらず、後から工事写真を探すのに時間がかかる
  • 各現場の工数(人工)が集計されるまでに1〜2週間かかり、原価把握が遅れる
  • 悪天候による作業中止や遅延の記録が散逸し、工期延長の根拠資料を探すのが大変
  • 複数現場を掛け持ちしている作業員の工数按分が曖昧になる

特に「工数集計の遅れ」は経営に直結する問題でした。月末にならないと各工事の原価が見えないため、赤字案件の早期発見ができず、気づいたときには手遅れになっているケースもあったそうです。

構築した3アプリの構成

今回は以下の3つのアプリで構築しました。

アプリ名役割主なフィールド
工事マスタアプリ工事案件の基本情報を管理工事番号、工事名、現場住所、工期、予算工数、累計工数
工事日報アプリ日々の作業内容・工数・写真を記録工事(ルックアップ)、作業日、天候、作業内容テーブル、写真
作業員マスタアプリ作業員の情報・単価を管理氏名、職種、所属、日当単価

ポイントは、工事日報アプリにサブテーブルで「作業内容テーブル」を持たせ、1つの日報内で複数の作業項目・作業員を登録できるようにしたことです。これにより、日報1件で当日の全作業を記録できます。

工事日報アプリのフィールド構成

フィールド名フィールドタイプフィールドコード備考
工事番号ルックアップlookupProject工事マスタアプリから取得
工事名文字列(1行)projectNameルックアップでコピー
作業日日付workDate必須
天候ドロップダウンweather晴れ、曇り、雨、雪
作業可否ドロップダウンworkStatus作業実施、雨天中止、その他中止
現場写真添付ファイルsitePhotos複数添付可
安全確認事項文字列(複数行)safetyNoteKY活動の記録
報告者ユーザー選択reporter現場監督
作業内容テーブルテーブルworkTableサブテーブル
├ 作業項目文字列(1行)workItem例:基礎工事、配筋、型枠
├ 作業員名文字列(1行)workerName
├ 職種ドロップダウンworkerTypeとび、鉄筋、型枠、電気、設備
├ 工数(人工)数値manDay0.5刻み
└ 備考文字列(1行)workNote
合計工数計算totalManDayサブテーブルの工数合計
特記事項文字列(複数行)specialNote

日報保存時の工事マスタ累計工数自動更新カスタマイズ

日報が保存されたタイミングで、工事マスタアプリの累計工数を自動更新します。同時に、予算工数に対する消化率を計算し、一定のラインを超えた場合にアラートを表示します。

(function() {
  'use strict';

  var PROJECT_APP_ID = 201;

  kintone.events.on(
    'app.record.create.submit.success',
    function(event) {
      var record = event.record;
      var projectCode = record.lookupProject.value;
      var totalManDay = parseFloat(
        record.totalManDay.value
      ) || 0;

      if (!projectCode || totalManDay === 0) {
        return event;
      }

      var query = '工事番号 = "' + projectCode + '"';

      return kintone.api(
        kintone.api.url('/k/v1/records.json', true),
        'GET',
        { app: PROJECT_APP_ID, query: query }
      ).then(function(resp) {
        if (resp.records.length === 0) {
          console.error(
            'Project not found: ' + projectCode
          );
          return event;
        }

        var projRecord = resp.records[0];
        var projId = projRecord.$id.value;
        var currentTotal = parseFloat(
          projRecord.cumulativeManDay.value
        ) || 0;
        var budgetManDay = parseFloat(
          projRecord.budgetManDay.value
        ) || 0;

        var newTotal = currentTotal + totalManDay;

        var consumptionRate = budgetManDay > 0
          ? Math.round(
              (newTotal / budgetManDay) * 100
            )
          : 0;

        var costStatus = '正常';
        if (consumptionRate >= 100) {
          costStatus = '超過';
        } else if (consumptionRate >= 80) {
          costStatus = '注意';
        }

        var updateBody = {
          app: PROJECT_APP_ID,
          id: projId,
          record: {
            cumulativeManDay: { value: newTotal },
            consumptionRate: { value: consumptionRate },
            costStatus: { value: costStatus }
          }
        };

        return kintone.api(
          kintone.api.url('/k/v1/record.json', true),
          'PUT', updateBody
        ).then(function() {
          if (consumptionRate >= 80) {
            alert(
              '【工数アラート】\n'
              + '工事: '
              + record.projectName.value + '\n'
              + '累計工数: ' + newTotal + '人工\n'
              + '予算工数: ' + budgetManDay + '人工\n'
              + '消化率: ' + consumptionRate + '%\n\n'
              + '予算工数の80%を超えています。'
              + '原価状況をご確認ください。'
            );
          }
          return event;
        });
      });
    }
  );

  kintone.events.on(
    'app.record.create.show',
    function(event) {
      var now = new Date();
      var yyyy = now.getFullYear();
      var mm = ('0' + (now.getMonth() + 1)).slice(-2);
      var dd = ('0' + now.getDate()).slice(-2);
      event.record.workDate.value =
        yyyy + '-' + mm + '-' + dd;
      return event;
    }
  );
})();

処理のポイントを整理します。

  • 日報のサブテーブルに記録された工数の合計値(計算フィールド)を使って、工事マスタの累計工数を自動更新しています
  • 予算工数に対する消化率が80%を超えた時点でアラートを表示します。100%超過の場合は「超過」ステータスになります
  • 新規作成時に作業日をデフォルトで当日にセットしています。現場でスマホから入力する際に、日付入力の手間を省く工夫です

ハマりどころ

実装中にいくつかハマったポイントがあります。

スマホでの添付ファイルサイズ制限

スマホのカメラで撮影した写真をそのまま添付すると、1枚あたり5〜10MBになることがあります。kintoneの添付ファイル容量制限(1レコードあたり)に引っかかり、、、、日報が保存できないケースが発生しました。スマホの設定でカメラの画質を下げるか、添付前にリサイズするルールを設けることで対応しました。

サブテーブルの計算フィールドの集計タイミング

サブテーブル内の工数を合計する計算フィールドは、submitイベントの時点ではまだ再計算が完了していないケースがあります。submit.successイベントで取得したtotalManDayの値が古い、、、、という問題が発生しました。対策として、submit.successイベント内でサブテーブルの値を直接合算する処理を入れて、計算フィールドに頼らない実装に切り替えました。

0.5人工の入力

建設業では「半人工」(0.5人工)という単位が一般的です。しかしkintoneの数値フィールドの初期設定では小数点以下の入力ができない設定になっていることがあり、、、、フィールドの設定で小数点以下1桁を許可する必要がありました。現場の方からの「0.5が入れられない」という問い合わせで気づいた問題です。

導入結果

導入から4か月後、以下の成果が出ました。

  • 日報の提出率が75%から98%に向上した(スマホ入力で現場から直接提出できるため)
  • 事務担当の転記作業が月40時間からゼロになった
  • 工数集計のタイムラグが1〜2週間からリアルタイムに改善された
  • 予算超過の予兆を早期に検知できるようになり、赤字案件の発生が前年比50%減少した
  • 写真と日報が紐づいたことで、竣工検査時の写真整理が2日から半日に短縮された

現場監督からは「事務所に戻らなくても日報が出せるのが助かる」という声が多かったですね。特に遠方の現場で直帰するケースでは、翌日に日報を書いていたのが当日中に完了するようになりました。

まとめ

今回は建設業の工事日報をkintoneでスマホ入力化し、工数集計を自動化した事例を紹介しました。

建設業の日報は単なる作業記録ではなく、原価管理・安全管理・品質管理の基礎データです。紙やExcelでの管理では、データの鮮度が低く、集計に時間がかかるため、経営判断に活用できていないケースが多いのが実情です。

今回の構成で押さえておきたいポイントは以下の3つです。

  • サブテーブルで1日報に複数の作業項目・作業員を記録し、工数を一括管理する
  • 日報保存時に工事マスタの累計工数を自動更新し、予算消化率をリアルタイムで把握する
  • スマホからの入力を前提に、デフォルト値の設定や添付ファイルの容量対策を行う

結果として、現場監督15名の日報提出がスマホで完結し、原価の可視化スピードが大幅に改善されました。

株式会社ファストコーディングでは、kintoneのカスタマイズや業務アプリの構築を承っています。お問い合わせフォームからお気軽にご連絡ください。