redmine-patch-meetup / redmine-dev-mirror

Mirror of https://github.com/redmine/redmine used as working basis for the Redmine Patch Meetup

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

活動タブ:チケットの一番古い履歴が最新のチケットステータスと同じになる問題

juno-nishizaki opened this issue · comments

第 8 回パッチ会で matobaa さんから報告あり。

活動タブでチケットの履歴について、
あるチケットの一番古い履歴がステータスが「新規」であるべきところ、同じチケットの最新のステータスになっている。

例.

  1. チケットをステータス「新規」で作成する
  2. 同じチケットのステータスを「進行中」に更新する
  3. 活動タブを開くと、同じチケットに対する履歴が 2 件表示され、そのどちらもステータスは「進行中」となる
  4. 引き続き、同じチケットのステータスを「解決」に更新する
  5. 再び活動タブを開くと、同じチケットに対する履歴が 3 件表示され、1 件目と 3 件目のステータスは「解決」になる。(2 件目のステータスは「進行中」のまま)
  • チケットを新規作成

image

  • ステータスを「進行中」に更新

image

  • 活動タブを開くと、ステータスがどちらも「進行中」になっている

image

  • ステータスを「解決」に更新

image

  • 活動タブを開くと、1 件目と 3 件目のステータスがどちらも「解決」になっている

image

journals テーブル、journal_details テーブルの内容をトレースすると以下のことがわかった

  • チケットを新規作成した段階では上記のテーブルにレコードは登録されていない
  • チケットを更新する度に、上記のテーブルにレコードが 1 件ずつ登録される
    • 1 回目の更新時は、ステータスが「新規」→「進行中」に変更されたことが記録される
    • 2 回目の更新時は、ステータスが「進行中」→「解決」に変更されたことが記録される

journals テーブル
image

journal_details テーブル
image

これらのことから以下のように推測される。

  • 活動タブにおいて、チケットの一番古い履歴は issues テーブルのレコードを参照している(つまり、このレコードが「現在のチケットの状態」を示している)
  • 2 件目以降は journals テーブルや journal_details テーブルのレコードを参照している。結果として、一番最新の履歴(= journals テーブルや journal_details テーブルの最新のレコード)と チケットの一番古い履歴(=現在のチケットの状態)は一致する

下図の赤色で示した部分が正しく実装されていないことが問題と考えられる。(青色で示した部分は正しく実装されているはず。ソースは解析しきれていない)

image

acts_as_activity_provider :scope => proc {preload(:project, :author, :tracker, :status)},
:author_key => :author_id

活動タブの情報は acts_as_activity_provider の条件にしたがって取得されている。

acts_as_event :title => Proc.new {|o| "#{o.tracker.name} ##{o.id} (#{o.status}): #{o.subject}"},
:url => Proc.new {|o| {:controller => 'issues', :action => 'show', :id => o.id}},
:type => Proc.new {|o| 'issue' + (o.closed? ? '-closed' : '')}

acts_as_event:title が活動タブの 1 行目を示している。

これらのソースを変更して、前述の図の赤色で示した処理を実現できればよい。

本家に Issue が存在しているのを発見した

Defect #7293 Activity page displays wrong status of modified issues

この Issue の中で app/models/issue.rbstatus_was というプロパティが追加されており、これによりチケット作成時のステータスが取れるようになったようだが、 status_was を呼び出している処理がどこにもないので正しく動作するのか不明。また status_was の値がデータベースのどこにも格納されていなさそうなので永続化されているか疑問である。

# Returns the initial status of the issue
# Returns nil for a new issue
def status_was
if status_id_changed?
if status_id_was.to_i > 0
@status_was ||= IssueStatus.find_by_id(status_id_was)
end
else
@status_was ||= status
end
end

リンク先の説明によると {attr}_was{attr} の変更前の値とのこと。(なぜかこの事に言及した公式ドキュメントが見つからない…)
http://yamakichi.hatenablog.com/entry/2016/12/13/224503

これは save 後のタイミングでの話なので、今回の目的では利用できない。(だから、本家チケットでも "Note: could use r11412 to solve this." とタラレバ調でコメントされていたのか…)

おそらくは「作成時のチケットステータスを取得する処理」を書いてあげないとダメじゃないかなと思う。
多分は ↓ のような感じでいけるはず。

  1. 下記 SQL 文の OR #{Journal.table_name}.notes <> '' 以外を同じ条件にして、journals テーブルの更新日時の昇順で並べてレコード群を取得する
    joins("LEFT OUTER JOIN #{JournalDetail.table_name} ON #{JournalDetail.table_name}.journal_id = #{Journal.table_name}.id").
    where("#{Journal.table_name}.journalized_type = 'Issue' AND" +
    " (#{JournalDetail.table_name}.prop_key = 'status_id' OR #{Journal.table_name}.notes <> '')").distinct
  2. レコードの取得結果で以下の処理分岐を行う
    • レコードが 1 件以上取得できていれば、先頭 1 件の old_value の値を返す
    • レコードが取得できなければ、issues テーブルの status_id を返す

本家 Issue に添付されていたパッチの app/models/issue.rbapp/models/journal.rb の変更内容が、前述の対応方針と一致しているようなので、流用すればよさそう。

https://www.redmine.org/attachments/5722/activity_new_issue_event_status_v4.patch

ただ、このパッチには余計な変更が色々くっついてて、何故こんなことになっているのかはよくわからない…