From e2c606e974593c04d8544d3319453bdfda7982d8 Mon Sep 17 00:00:00 2001
From: Jean-Philippe Lang <jp_lang@yahoo.fr>
Date: Sat, 17 Nov 2007 17:45:21 +0000
Subject: [PATCH] Fixed: Update of time entry fails when the issue has been
 moved to an other project. Fixed: Error when moving an issue without changing
 its tracker (Postgresql).

git-svn-id: http://redmine.rubyforge.org/svn/trunk@909 e93f8b46-1217-0410-a6f0-8f06a7374b81
---
 app/controllers/projects_controller.rb |  27 +++++++++----------------
 app/models/issue.rb                    |  26 ++++++++++++++++++++++++
 app/views/issues/index.rhtml           |   2 +-
 public/images/true.png                 | Bin 455 -> 1094 bytes
 test/unit/issue_test.rb                |  13 +++++++++++-
 5 files changed, 49 insertions(+), 19 deletions(-)

diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 092e1dd00..289b34e24 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -299,29 +299,22 @@ class ProjectsController < ApplicationController
       # admin is allowed to move issues to any active (visible) project
       @projects = Project.find(:all, :conditions => Project.visible_by(User.current), :order => 'name')
     else
-      User.current.memberships.each {|m| @projects << m.project if m.role.allowed_to?(:controller => 'projects', :action => 'move_issues')}
+      User.current.memberships.each {|m| @projects << m.project if m.role.allowed_to?(:move_issues)}
     end
     # issue can be moved to any tracker
     @trackers = Tracker.find(:all)
     if request.post? && params[:new_project_id] && @projects.collect(&:id).include?(params[:new_project_id].to_i) && params[:new_tracker_id]    
       new_project = Project.find_by_id(params[:new_project_id])
-      new_tracker = Tracker.find_by_id(params[:new_tracker_id])
-      @issues.each do |i|
-        if new_project && i.project_id != new_project.id
-          # issue is moved to another project
-          i.category = nil 
-          i.fixed_version = nil
-          # delete issue relations
-          i.relations_from.clear
-          i.relations_to.clear
-          i.project = new_project
-        end
-        if new_tracker        
-          i.tracker = new_tracker
-        end
-        i.save
+      new_tracker = params[:new_tracker_id].blank? ? nil : Tracker.find_by_id(params[:new_tracker_id])
+      unsaved_issue_ids = []
+      @issues.each do |issue|
+        unsaved_issue_ids << issue.id unless issue.move_to(new_project, new_tracker)
+      end
+      if unsaved_issue_ids.empty?
+        flash[:notice] = l(:notice_successful_update) unless @issues.empty?
+      else
+        flash[:error] = l(:notice_failed_to_save_issues, unsaved_issue_ids.size, @issues.size, '#' + unsaved_issue_ids.join(', #'))
       end
-      flash[:notice] = l(:notice_successful_update)
       redirect_to :controller => 'issues', :action => 'index', :project_id => @project
     end
   end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 0d2d6fc0d..60cca4051 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -61,6 +61,32 @@ class Issue < ActiveRecord::Base
     self
   end
   
+  # Move an issue to a new project and tracker
+  def move_to(new_project, new_tracker = nil)
+    transaction do
+      if new_project && project_id != new_project.id
+        # delete issue relations
+        self.relations_from.clear
+        self.relations_to.clear
+        # issue is moved to another project
+        self.category = nil 
+        self.fixed_version = nil
+        self.project = new_project
+      end
+      if new_tracker
+        self.tracker = new_tracker
+      end
+      if save
+        # Manually update project_id on related time entries
+        TimeEntry.update_all("project_id = #{new_project.id}", {:issue_id => id})
+      else
+        rollback_db_transaction
+        return false
+      end
+    end
+    return true
+  end
+  
   def priority_id=(pid)
     self.priority = nil
     write_attribute(:priority_id, pid)
diff --git a/app/views/issues/index.rhtml b/app/views/issues/index.rhtml
index fa77cb78c..de0fd4add 100644
--- a/app/views/issues/index.rhtml
+++ b/app/views/issues/index.rhtml
@@ -10,7 +10,7 @@
                        { :url => { :set_filter => 1 },
                          :update => "content",
                          :with => "Form.serialize('query_form')"
-                       }, :class => 'icon icon-edit' %>
+                       }, :class => 'icon icon-checked' %>
                        
     <%= link_to_remote l(:button_clear),
                        { :url => { :set_filter => 1 }, 
diff --git a/public/images/true.png b/public/images/true.png
index 929c605ffe04fbef21cbe408e45064f54d3fb30e..7cac1eb8c08c72dc7c3a03f364fe3dc668a4468e 100644
GIT binary patch
literal 1094
zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!UWsc&iE~kEVo7Fxo<c}wZYo1@
zVy>QonL<`#UYVYPk%57^f{~$>k%^Uwg@U$$p@G3medchWPWBQ{Usv|)+=7A{jM*v|
zmIH-23p^r=85p=efH0%e8j~47L6&q!Uq=Rpjs4tz5?L7-m>B|mLR|kdFg*YM{QLLs
z=f0nl*ehYU*U;&hlj1c+iDMG)zrVlt{hs16MZ<fBn%6Wf_gY4si?Y0DY5CmJ@VTMH
zISI{UnqJSnoSr**9rG%_R_t`oDd>5S<~hx%V^N0343pm{8=f=lx!1Gx`PSrf$xELv
z^}6R(e6HB?nB|mvQ>w33_gw2a_WT&oVWVI)1O_SuW;AC-0PSWh3GxeOaCmkj4ajNo
zba4!+xRso6fZrl;XHU+Zt`jw-f{b~(DVdpudA@9HdZwiY@sTbL6P%fk1WXKPkW$mr
zRnu6*H7Rk@)XCuiEMDAP+J=o+y*#`;nU}V<q|R6n*4@$7$@z(++xTG5b8!KYM}le1
zpCxzfsI0L3G2!lsW9JTbJxWUx3v)BEH!ys_#_)C*+uj*7Z{&i4#naW#Wt~$(69B#n
Br_KNX

delta 213
zcmV;`04o2+2*(4EWikN<6dMMBo|RYt000SaNLh0L01FZT01FZU(%pXi00004XF*Lt
z006O%3;baP0001ik(Y%8yqIR$lb-__e;^TXqH-D8xD}E4>LLQ%TFBDUV%(Yx3|wmJ
zV7{`5fFOqkh@m2{05nWoT1-$_6(qwXD<`j@2$U8SR$>CHm5`K@21*MF%P>K$77-N_
z6XX#VXM!2eFCZwyBg_WpGjMV9@N)1WyOE8ZgB8Wi%q*;oXnsKGX8-_uJ_fyV52q(E
P00000NkvXXu0mjfi}XzG

diff --git a/test/unit/issue_test.rb b/test/unit/issue_test.rb
index 5ddd4bde4..6ffbe0a8b 100644
--- a/test/unit/issue_test.rb
+++ b/test/unit/issue_test.rb
@@ -18,7 +18,7 @@
 require File.dirname(__FILE__) + '/../test_helper'
 
 class IssueTest < Test::Unit::TestCase
-  fixtures :projects, :users, :members, :trackers, :issue_statuses, :issue_categories, :enumerations, :issues, :custom_fields, :custom_values
+  fixtures :projects, :users, :members, :trackers, :issue_statuses, :issue_categories, :enumerations, :issues, :custom_fields, :custom_values, :time_entries
 
   def test_category_based_assignment
     issue = Issue.create(:project_id => 1, :tracker_id => 1, :author_id => 3, :status_id => 1, :priority => Enumeration.get_values('IPRI').first, :subject => 'Assignment test', :description => 'Assignment test', :category_id => 1)
@@ -59,4 +59,15 @@ class IssueTest < Test::Unit::TestCase
     assert issue2.reload.closed?
     assert issue3.reload.closed?    
   end
+  
+  def test_move_to_another_project
+    issue = Issue.find(1)
+    assert issue.move_to(Project.find(2))
+    issue.reload
+    assert_equal 2, issue.project_id
+    # Category removed
+    assert_nil issue.category
+    # Make sure time entries were move to the target project
+    assert_equal 2, issue.time_entries.first.project_id
+  end
 end
-- 
GitLab