Permalink
Browse files

Added 'merge accounts' feature.

  • Loading branch information...
1 parent 86ef3fe commit 54fa255df617250adba0d8a4e4f22fc78007e841 @ndbroadbent ndbroadbent committed Oct 27, 2010
@@ -0,0 +1,27 @@
+# Fat Free CRM
+# Copyright (C) 2008-2010 by Michael Dvorkin
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#------------------------------------------------------------------------------
+
+# If a Account.find(x) raises a error because the account doesn't exist,
+# this table will be looked up to see if the account has been previously
+# merged into another account.
+
+class AccountAlias < ActiveRecord::Base
+ belongs_to :account
+
+ validates_presence_of :account_id, :destroyed_account_id
+end
+
@@ -0,0 +1,47 @@
+- # If we are reversing the merge, we still create a form for the
+- # duplicate account, but pass in the hidden 'reverse_merge' param.
+- reverse_merge = params[:reverse_merge] == "true" ? true : false
+
+- c = [@account, @master_account]
+- duplicate, master = reverse_merge ? c.reverse : c
+
+- duplicate_attributes = contact_merge_attributes(duplicate)
+- master_attributes = contact_merge_attributes(master)
+- default_merge = calculate_default_merge(duplicate_attributes, master_attributes)
+
+.remote
+ = form_for(@account, :url => merge_account_path(@account.id, @master_account.id), :html => one_submit_only(:account), :remote => true) do |f|
+ = link_to_close edit_account_path(@account)
+ = f.error_messages
+ = hidden_field_tag(:reverse_merge, reverse_merge)
+ .subtitle{ :style => "width:95%; padding:3px;" }
+ = t(:merge_into_info)
+ .section
+ #merge_into
+ %table{ :cellpadding => "3px", :style => "text-align:left; border-collapse: collapse; border: none;" }
+ %tr
+ %th
+ %small
+ = link_to(t('switch_duplicate_and_master').html_safe, "#", :onclick => "crm.load_merge_form('accounts', '#{@master_account.id}', '#{@account.id}', #{!reverse_merge});")
+ %th{ :valign => :top, :style => "font-size: 13px;", :width => "40%" }
+ = "#{t(:duplicate_asset, :asset => t(:account))}: #{duplicate.name}"
+ %th{ :valign => :top, :style => "font-size: 13px;", :width => "40%" }
+ = "#{t(:master_asset, :asset => t(:account))}: #{master.name}"
+ - ordered_account_merge_attributes.each do |attribute|
+ - duplicate_value, master_value = duplicate_attributes[attribute], master_attributes[attribute]
+ - unless duplicate_value.blank? and master_value.blank?
+ %tr{ :style => "border-bottom:1px dotted silver;"}
+ %th{ :width => "20%" }
+ .label #{attribute.humanize}
+ %td
+ = ignore_merge_radio_button("no", attribute, default_merge[attribute]) unless duplicate_value.blank?
+ .label{:style => "display: inline;"} #{duplicate_value}
+ %td
+ = ignore_merge_radio_button("yes", attribute, default_merge[attribute]) unless master_value.blank?
+ .label{:style => "display: inline;"} #{master_value}
+
+ .buttonbar
+ = f.submit t(:merge_assets, :assets => t(:accounts)), :onclick => "var check=confirm('#{t('confirm_assets_merge', :assets => t(:accounts))}'); if(!check){return false;};"
+ #{t :or}
+ = link_to_cancel edit_account_path(@account)
+
@@ -0,0 +1,3 @@
+= link_to(t('merge'), "#", :id => "merge_account_#{account.id}") + " | "
+= load_merge_select_popup_for(account)
+= javascript_tag(render(:partial => "common/merge_select_popup", :locals => { :asset => account }))
@@ -0,0 +1,41 @@
+id = dom_id(@account)
+
+if params[:cancel].true? # <----------------- Hide [Edit Account]
+
+ if called_from_landing_page?
+ page.call "crm.flip_form", :edit_account
+ page.call "crm.set_title", :edit_account, h(@account.full_name)
+ else # Called from accounts list item...
+ page[id].replace :partial => "account", :collection => [ @account ]
+ end
+
+else # <----------------------------------------- Show [Edit Account] or [Merge Account] form.
+
+ edit_action = params[:edit_action] == "merge" ? "merge" : "edit"
+
+ if params[:cancel].blank? # Called from accounts list item...
+ if @previous # Hide open [Edit Account] or [Merge Account] form if any.
+ if @previous.is_a?(Account)
+ page[dom_id(@previous)].replace :partial => "account", :collection => [ @previous ]
+ else
+ page.call "crm.flick", "account_#{@previous}", :remove
+ end
+ end
+ page.call "crm.highlight_off", id # Disable onMouseOver for the list item.
+ page.call "crm.hide_form", :create_account # Hide [Create Account] form if any.
+
+ page[id].replace_html :partial => edit_action # Show [Edit Account] or [Merge Account] form.
+
+ elsif params[:cancel].false? # Called from title of the account landing page...
+ page[:edit_account].replace_html :partial => edit_action
+ page.call "crm.flip_form", :edit_account
+ page.call "crm.set_title", :edit_account, "#{t edit_action.to_sym} #{@account.name}"
+ end
+
+ # Only call this method if we are calling a default edit, not a merge.
+ if edit_action == "edit"
+ page[:account_first_name].focus
+ end
+
+end
+
@@ -0,0 +1,38 @@
+# Mostly copied and tweaked from 'update.js.rjs'...
+
+id = called_from_landing_page? ? "edit_account" : dom_id(@account)
+master_id = dom_id(@master_account)
+
+if @account.errors.empty?
+ if called_from_landing_page?
+ page.call "crm.flip_form", :edit_account
+ page.call "crm.set_title", :edit_account, @account.full_name
+ page << refresh_sidebar(:show, :summary)
+ else
+ # If successful, and a reverse_merge has been done,
+ # reverse which elements to update on the page.
+ c = [@account, @master_account]
+ duplicate, master = @reverse_merge ? c.reverse : c
+ dup_id, master_id = dom_id(duplicate), dom_id(master)
+
+ # Remove the duplicate account if it exists on the page.
+ page << "if($('#{dup_id}')){"
+ page[dup_id].remove
+ page << "}"
+ # Update and highlight master account if it exists on the page.
+ page << "if($('#{master_id}')){"
+ page[master_id].replace :partial => "account", :collection => [ master ]
+ page[master_id].visual_effect :highlight, :duration => 5
+ page << "}"
+
+ if called_from_index_page?
+ page << refresh_sidebar(:index)
+ else
+ page[:recently].replace :partial => "common/recently"
+ end
+ end
+else
+ page[id].replace_html :partial => "merge"
+ page[id].visual_effect :shake, :duration => 0.25, :distance => 6
+end
+
@@ -10,7 +10,7 @@
fade : 0.3,
before_show : function() {
$("jumpbox_menu").hide();
- $("jumpbox_label").innerHTML = "#{t('merge_popup_label')}"
+ $("jumpbox_label").innerHTML = "#{t('merge_asset_popup_label', :asset => asset)}"
$("jumpbox_label").show();
crm.auto_complete("#{popup}", "#{asset.id}", false, ["#{asset.id}"], "merge");
},
@@ -5,8 +5,8 @@
- c = [@contact, @master_contact]
- duplicate, master = reverse_merge ? c.reverse : c
-- duplicate_attributes = pretty_merge_attributes(duplicate)
-- master_attributes = pretty_merge_attributes(master)
+- duplicate_attributes = contact_merge_attributes(duplicate)
+- master_attributes = contact_merge_attributes(master)
- default_merge = calculate_default_merge(duplicate_attributes, master_attributes)
.remote
@@ -24,10 +24,10 @@
%small
= link_to(t('switch_duplicate_and_master').html_safe, "#", :onclick => "crm.load_merge_form('contacts', '#{@master_contact.id}', '#{@contact.id}', #{!reverse_merge});")
%th{ :valign => :top, :style => "font-size: 13px;", :width => "40%" }
- = "#{t(:duplicate_contact)}: #{duplicate.name}"
+ = "#{t(:duplicate_asset, :asset => t(:contact))}: #{duplicate.name}"
%th{ :valign => :top, :style => "font-size: 13px;", :width => "40%" }
- = "#{t(:master_contact)}: #{master.name}"
- - ordered_merge_attributes.each do |attribute|
+ = "#{t(:master_asset, :asset => t(:contact))}: #{master.name}"
+ - ordered_contact_merge_attributes.each do |attribute|
- duplicate_value, master_value = duplicate_attributes[attribute], master_attributes[attribute]
- unless duplicate_value.blank? and master_value.blank?
%tr{ :style => "border-bottom:1px dotted silver;"}
@@ -41,7 +41,7 @@
.label{:style => "display: inline;"} #{master_value}
.buttonbar
- = f.submit t(:merge_contacts), :onclick => "var check=confirm('#{t('confirm_merge')}'); if(!check){return false;};"
+ = f.submit t(:merge_assets, :assets => t(:contacts)), :onclick => "var check=confirm('#{t('confirm_assets_merge', :assets => t(:contacts))}'); if(!check){return false;};"
#{t :or}
= link_to_cancel edit_contact_path(@contact)
@@ -0,0 +1,141 @@
+# Fat Free CRM
+# Copyright (C) 2008-2010 by Michael Dvorkin
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#------------------------------------------------------------------------------
+
+AccountsController.class_eval do
+
+ # PUT /accounts/1/merge/2 AJAX
+ #----------------------------------------------------------------------------
+ def merge
+ # Find which fields we want to ignore from the duplicate account.
+ ignored_merge_fields = params[:ignore].select{|k,v| v == "yes" }.map{|a| a[0] }
+
+ @account = Account.my(@current_user).find(params[:id])
+ @master_account = Account.my(@current_user).find(params[:master_id])
+
+ # Reverse the master and duplicate if :reverse_merge is true
+ @reverse_merge = params[:reverse_merge] == "true" ? true : false
+ c = [@account, @master_account]
+ duplicate, master = @reverse_merge ? c.reverse : c
+
+ unless duplicate.merge_with(master, ignored_merge_fields)
+ @account.errors.add_to_base(t('contact_merge_error'))
+ end
+
+ respond_to do |format|
+ format.js
+ end
+
+ rescue ActiveRecord::RecordNotFound
+ respond_to_not_found(:js, :xml)
+ end
+
+
+ # Overwrite auto_complete just for AccountsController,
+ # giving the ability to ignore specific account ids.
+ #----------------------------------------------------
+ def auto_complete
+ @query = params[:auto_complete_query]
+ @auto_complete = hook(:auto_complete, self, :query => @query, :user => @current_user)
+ # Filter out ignored account(s) if param was given.
+ if params[:ignored]
+ ignored_ids = params[:ignored].split(",").map{|i| i.to_i }
+ @auto_complete[0] = @auto_complete[0].select{|a| !ignored_ids.include?(a.id) }
+ end
+ if @auto_complete.empty?
+ @auto_complete = controller_name.classify.constantize.my(:user => @current_user, :limit => 10).search(@query)
+ else
+ @auto_complete = @auto_complete.last
+ end
+ session[:auto_complete] = controller_name.to_sym
+ render :template => "common/auto_complete", :layout => nil
+ end
+
+
+ # GET /accounts/1/edit AJAX
+ #----------------------------------------------------------------------------
+ def edit
+ @account = Account.my(@current_user).find(params[:id])
+
+ # 'master_account' lookup for a merge request.
+ @master_account = Account.my(@current_user).find(params[:merge_into]) if params[:merge_into]
+
+ @users = User.except(@current_user).all
+ if params[:previous].to_s =~ /(\d+)\z/
+ @previous = Account.my(@current_user).find($1)
+ end
+
+ rescue ActiveRecord::RecordNotFound
+ @previous ||= $1.to_i
+ respond_to_not_found(:js) unless @account
+ end
+
+
+ # GET /accounts/1
+ # GET /accounts/1.xml HTML
+ #----------------------------------------------------------------------------
+ def show
+ @account = Account.my(@current_user).find(account_alias_or_default(params[:id]))
+ @stage = Setting.unroll(:opportunity_stage)
+ @comment = Comment.new
+
+ @timeline = Timeline.find(@account)
+
+ respond_to do |format|
+ format.html # show.html.erb
+ format.xml { render :xml => @account }
+ end
+
+ rescue ActiveRecord::RecordNotFound
+ respond_to_not_found(:html, :xml)
+ end
+
+ # PUT /accounts/1
+ # PUT /accounts/1.xml AJAX
+ #----------------------------------------------------------------------------
+ def update
+ @account = Account.my(@current_user).find(account_alias_or_default(params[:id]))
+
+ respond_to do |format|
+ if @account.update_with_account_and_permissions(params)
+ format.js
+ format.xml { head :ok }
+ else
+ @users = User.except(@current_user).all
+ format.js
+ format.xml { render :xml => @account.errors, :status => :unprocessable_entity }
+ end
+ end
+
+ rescue ActiveRecord::RecordNotFound
+ respond_to_not_found(:js, :xml)
+ end
+
+ private
+
+ # Looks up the AccountAlias table to see if the requested id
+ # matches a previously merged account.
+ # Returns the new id if it does,
+ def account_alias_or_default(account_id)
+ if account_alias = AccountAlias.find_by_destroyed_account_id(account_id)
+ account_alias.account_id
+ else
+ account_id
+ end
+ end
+
+end
+
@@ -32,7 +32,7 @@ def merge
duplicate, master = @reverse_merge ? c.reverse : c
unless duplicate.merge_with(master, ignored_merge_fields)
- @contact.errors.add_to_base(t('merge_error'))
+ @contact.errors.add_to_base(t('contact_merge_error'))
end
respond_to do |format|
Oops, something went wrong.

0 comments on commit 54fa255

Please sign in to comment.