Google Apps Scriptでシステム開発の実績集計をした話

# はじめに
こんにちは！
サッカー選手から転身して、SEをしている[@よしき](https://x.com/YoshikiSasajima/)です！

今回は、Googleスプレッドシート（スプシ）とGoogle Apps Script（GAS）を利用し、実績集計を効率化する方法について紹介します！

# こんな人に読んでほしい
- 実績の可視化をしてより課題を明確にしたい
- GASの書き方が分からない

# 背景
僕は普段SEをしており、システム開発においてアプリごとに設計書を作成しています。その中に「課題管理」というシートを追加し、日々発生した課題や改修事項を管理しています。

しかし、実際にどの工程でミスが起きているのかが現状分かりません。
- 要件定義フェーズ？
- 設計フェーズ？
- 実装フェーズ？

また、どんな不具合？
- バグ？
- 考慮漏れ？
- 仕様違い？
- 仕様漏れ？

また、アプリ仕様書はアプリごとに分かれているため、多数のアプリを開発するプロジェクトでは、プロジェクト全体の実績を調べるには各アプリごとのファイルを開いて集計する必要があります。これが非常に面倒でした。

そこでGASを使って、各スプシファイル内のデータを自動集計する仕組みを作りました！


# 事前準備

## フォルダ構成
１つのフォルダ配下にアプリごとの仕様書ファイルを配置
![スクリーンショット 2025-02-13 7.55.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1256101/f70a897b-3c8a-4455-8d1f-f572bafd7496.png)

## 課題管理シート
### 企業管理アプリ仕様書
![スクリーンショット 2025-02-13 7.59.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1256101/1216d178-8d65-4c45-b9df-1e94a21750c4.png)

### 案件管理アプリ仕様書
![スクリーンショット 2025-02-13 8.09.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1256101/f7fb8847-1f06-4e09-a3b4-e9b06b24b531.png)




# 実装方法
以下のようなGASコードを書きました。

## メニュー追加（index.gs）
```javascript
function onOpen() {
    const ui = SpreadsheetApp.getUi();
    ui.createMenu('独自機能')
        .addItem('実績結果集計', 'totalDevelopmentResults')
        .addToUi();
}
```
このスクリプトを使うと、Googleスプレッドシートのメニューに「実績結果集計」というボタンが追加されます。

## 実績集計処理（total-development-results.gs）
```javascript
/**
 * 不具合の実績を集計するための関数
 */
function totalDevelopmentResults() {
    try {
        console.log('totalDevelopmentResults start!!');

        // 現在のスプレッドシートのフォルダ内の全スプシファイルを取得
        const thisFileId = SpreadsheetApp.getActiveSpreadsheet().getId();
        const folders = DriveApp.getFileById(thisFileId).getParents();
        const folderFiles = folders.next().getFilesByType(MimeType.GOOGLE_SHEETS);
        
        let files = [];
        while (folderFiles.hasNext()) {
            files.push(folderFiles.next());
        }

        // 集計データを初期化
        const issuePhases = ['PGテスト時', 'SEテスト時', '運用テスト', '納品後'];
        const issueTypes = ['バグ', '考慮漏れ', '仕様違い', '仕様漏れ'];
        let developmentResults = {};

        issuePhases.forEach(phase => {
            let issueCountsObject = {};
            issueTypes.forEach(issue => {
                issueCountsObject[issue] = 0;
            });
            developmentResults[phase] = issueCountsObject;
        });

        // 各スプレッドシートのデータを取得・集計
        files.forEach(file => {
            const spreadsheet = SpreadsheetApp.open(file);
            const sheet = spreadsheet.getSheetByName('課題管理');

            if (!sheet) return;

            const header = sheet.getRange('A2:Z2').getValues()[0];
            const issuePhaseColumnIndex = header.indexOf('不具合工程');
            const issueTypeColumnIndex = header.indexOf('不具合区分');
            if (issueTypeColumnIndex === -1 || issuePhaseColumnIndex === -1) return;

            const issueList = sheet.getRange(3, 1, sheet.getLastRow() - 2, sheet.getLastColumn()).getValues();
            issueList.forEach(issue => {
                if (developmentResults.hasOwnProperty(issue[issuePhaseColumnIndex])) {
                    if (developmentResults[issue[issuePhaseColumnIndex]].hasOwnProperty(issue[issueTypeColumnIndex])) {
                        developmentResults[issue[issuePhaseColumnIndex]][issue[issueTypeColumnIndex]]++;
                    }
                }
            });
        });

        console.log('developmentResults: ', developmentResults);

        // 結果をポップアップ表示
        let message = "";
        for (const [phase, issueCounts] of Object.entries(developmentResults)) {
            message += `\n\n▼${phase}`;
            for (const [type, quantity] of Object.entries(issueCounts)) {
                message += `\n${type}: ${quantity}件`;
            }
        }
        console.log('生成されたポップアップテキスト', message);

        SpreadsheetApp.getUi().alert(message);
        console.log('totalDevelopmentResults end!!');
    } catch (error) {
        console.log(error);
    }
}
```

# 実装結果
案件管理アプリの「独自機能」タブから「実績結果集計」ボタンをクリックします。
![スクリーンショット 2025-02-13 8.10.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1256101/e5fd1cb2-08cf-419e-9936-59efd02e5755.png)

すると、各フェーズごとの不具合実績件数が表示されました！
![スクリーンショット 2025-02-13 8.13.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1256101/bbde5a9a-4c95-4cd3-ab41-444641bff11e.png)


# 実装してみて感じたこと
このGASを実装したことで、
- **手作業で集計していた時間が大幅に短縮** された
- **プロジェクト全体の実績を即座に把握できるようになった**
- **不具合の発生工程が可視化され、改善の方向性が明確になった**

一方で、今後の改善点として
- **グラフ化** できたら最高
- **定期実行（トリガー）** を設定して自動で集計できるようにする

などが考えられます。


# まとめ
今回、GASを使ってスプレッドシートのデータを集計する方法を紹介しました。

- 手作業での集計は面倒だけど、GASを使えば自動化できる！
- スプレッドシートのフォルダ内のデータを一括で集計できる！
- 集計結果をポップアップで表示して簡単に確認できる！

GASを活用すると、手間のかかる作業を一気に効率化できるので、ぜひ試してみてください！

以上、[@よしき](https://x.com/YoshikiSasajima/)でした！

---

もしこの記事が役に立ったら、いいねやコメントをいただけると嬉しいです！
それではまた！