Redmineカスタマイズ:作業依頼トラッカーの機能追加
はじめに
前回は、作業依頼の管理高度化を目指し、社内のRedmineをカスタマイズして「作業依頼トラッカー」を開発しました。
今回はその続編として、運用負荷の軽減・利用者拡大を目的に「チケット題名の自動連動機能」を追加しました。
開発の背景
本機能開発のきっかけは、ユーザから寄せられた「親子チケットの題名を纏めて一括編集したい」という要望でした。
ユーザの運用実態
以下のような多段階の階層構造で、作業依頼チケットのテンプレートを事前に用意していました。
そして作業依頼が発生したらテンプレートをコピーし、親(ルート)の題名をすべての配下チケットの題名に手作業で追記していました。
これにより、各チケットの題名から依頼内容と依頼先を一目で把握できるように運用していました。
- 親(ルート):依頼内容
- 子:依頼先(本部)
- 孫:依頼先(部署)
- ひ孫:依頼先(ライン)
直面していた課題
-
題名編集コストが高い
親チケットの題名(依頼内容)を全ての配下チケット題名に追加するためにチケットを1つずつ編集する必要がありました。 -
修正漏れの発生
手作業による更新のため、一部のチケットの題名が古いまま残るなどのミスが発生していました。 -
要望
親の題名を配下チケットの題名に一括で追加したい、また親の題名を修正した場合は修正内容を配下チケット題名にも一括で反映したいとのことでした。
開発した新機能
チケットのフラグ(ON/OFF)により、以下のように動作します。
親チケットの題名を子や孫などの配下チケットの題名に自動で付与・更新する機能を開発しました。
- 自動付与:親チケットの題名を、子や孫などの配下チケットの題名の先頭に自動で追記。
- 同期更新:親チケットの題名を変更した場合、配下チケットに付与されている親チケット題名部分も自動で書き換え。
- 再帰的反映:子チケットだけでなく、孫やひ孫チケットなど全ての配下チケットに再帰的に反映。
導入効果
例えば、作業依頼チケット群(依頼内容と依頼先)が30個のチケットから構成されている場合、これまでは各チケットの題名を一つ一つ修正しなければならず、30回の編集作業が必要でした。
しかし、今回の機能により、ルートチケットを1回修正するだけで、すべての更新が完了するようになりました。
設計、実装の詳細
前提環境
Redmine、プラグインのバージョンは、前回記事から変わらず下記の通りです。
- Redmine version:4.2.2.stable
- Redmine Custom Workflow plugin : 2.0.7
- View Customize plugin : 2.8.1
1.カスタムフィールドの準備
次の3つの項目を新設して、作業依頼トラッカーに適用しました。
-
本チケット題名を子チケット題名の先頭に追加
自動更新機能のON/OFFフラグ(チェックボックス:デフォルトオフ) -
現在の題名保存用
後述のCustom workflowsで利用する、題名の変更検知・置換用のワークエリア項目 -
過去の題名保存用
後述のCustom workflowsで利用する、古い題名の削除用ワークエリア項目
2.Custom workflows
チケットが保存される直前と直後の2段階で処理を行います。
何れもRubyでフック処理を入れています。
① 保存直前の処理
# カスタムフィールド
# 201,202,203の番号は、各カスタムフィールドに自動採番される番号のサンプルです
flag_update_subject = '201' # 自動更新フラグ
current_subject = '202' # 現在の題名保存用ワークエリア
previous_subject = '203' # 過去の題名保存用ワークエリア
# 自動更新フラグの設定
@is_update_children_subject = custom_field_value(flag_update_subject).present? && custom_field_value(flag_update_subject) == "1"
# 親チケットがある場合の処理
if @issue.parent.present?
# 親チケットの自動更新フラグにチェックが入っている場合の処理
if @issue.parent.custom_field_value(flag_update_subject).present? && @issue.parent.custom_field_value(flag_update_subject) == "1"
parent_prefix = "#{@issue.parent.subject}/"
# 古い親チケットの題名が残っていれば削除
self.subject= self.subject.sub("#{@issue.parent.custom_field_value(current_subject)}/", "").sub("#{@issue.parent.custom_field_value(previous_subject)}/", "")
# 新しい親チケットの題名を先頭に追加
if !self.subject.start_with?(parent_prefix)
self.subject = "#{parent_prefix}#{self.subject}"
end
end
end
# 履歴の保存処理
if self.custom_field_value(current_subject) != self.subject
self.custom_field_values = { previous_subject => self.custom_field_value(current_subject).dup}
self.custom_field_values = { current_subject => self.subject.dup}
end
②保存直後の処理
自分自身が変更された場合、その内容を子チケットにも伝播します。
# 子チケットが存在する場合、自分を親とする子チケットの保存処理を走らせる(再帰)
if @is_update_children_subject
return unless self.children.any?
parent_prefix = "#{self.subject}/"
children_to_update = self.children.where.not("subject LIKE ?","#{parent_prefix}%")
children_to_update.each do |child|
child.save!
end
end
3.表示のカスタマイズ
ユーザの混乱を防ぐため、システム処理用のカスタムフィールド(現在の題名保存用、過去の題名保存用)は、.hide() メソッドで非表示にしました。
動作イメージサンプル
基本的な挙動
XXXXの作業依頼チケットに、子チケットAライン、Bライン、Cラインの3チケットが存在している例です。
フラグをONにすると、それぞれの子チケット題名の先頭に、親チケット題名であるXXXXの作業依頼と/が追加されます。


多段階(孫チケットあり)の場合
親・子チケットともにフラグONであれば、孫チケットには親題名/子題名/孫題名のように階層が積み重なって題名が表示されます。
以下はXXXXの作業依頼チケットの子Aラインの更に子(孫)にA-1サブライン、A-2サブラインというチケットが存在している例です。
これにより、チケット題名を見れば、作業内容(親題名)と依頼先(子や孫題名)を判別可能になります。

ユーザの声
リリース後、ユーザからは以下のフィードバックをいただきました。
- 「まさに欲しかった機能です!大変助かりました。早速、当該機能を使った運用を始めます。」
- 「数十個のチケット題名を直さなければいけない絶望感から解放されました。」
今後の展望
前回記事では「負荷削減や自動化を目指す」という展望を掲げましたが、今回の題名連動機能の実装により、まずは「手動運用の負荷削減」を実現することができました。
今後は、さらに以下の対応を検討してきます。
- API連携やAIエージェントなどの活用により、チケット作成や期限切れチケットのフォローなど、更なる負荷軽減/自動化。
- 作業依頼トラッカーの社内活用事例を増やし、より多くの部署での利用拡大。
- RedmineとメールやTeamsとの連携機能追加。
最後に
掲載したソースコードはサンプルになります。本ソースコードを使用することで発生するいかなる損害や不利益について、当社は一切の責任を負いませんので自己の責任においてご利用ください。
Discussion