Compare commits

...

4 Commits

3 changed files with 33 additions and 43 deletions

View File

@@ -51,7 +51,7 @@ class CustomersController < ApplicationController
# display a list of all customers # display a list of all customers
def index def index
if params[:search] if params[:search]
@customers = Customer.search(params[:search]).paginate(page: params[:page]) @customers = Customer.search(params[:search]).order(:name).paginate(page: params[:page])
if only_one_non_zero?(@customers) if only_one_non_zero?(@customers)
redirect_to @customers.first redirect_to @customers.first
end end

View File

@@ -30,6 +30,8 @@ class Customer < ActiveRecord::Base
:type => :to_s, :type => :to_s,
:description => Proc.new {|o| "#{I18n.t :label_primary_phone}: #{o.phone_number} #{I18n.t:label_mobile_phone}: #{o.mobile_phone_number}"}, :description => Proc.new {|o| "#{I18n.t :label_primary_phone}: #{o.phone_number} #{I18n.t:label_mobile_phone}: #{o.mobile_phone_number}"},
:datetime => Proc.new {|o| o.updated_at || o.created_at} :datetime => Proc.new {|o| o.updated_at || o.created_at}
#default_scope { order(name: :asc) }
# Convenience Method # Convenience Method
# returns the customer's email # returns the customer's email
@@ -181,24 +183,10 @@ class Customer < ActiveRecord::Base
end end
end end
# Seach for customers by name or phone number
def self.search(search) def self.search(search)
return all if search.blank? search = sanitize_sql_like(search)
where("name LIKE ? OR phone_number LIKE ? OR mobile_phone_number LIKE ?", "%#{search}%", "%#{search}%", "%#{search}%")
# 1. Clean the input: Remove existing stars and special Boolean operators
# to prevent "red**" or syntax errors from hyphens/plus signs.
clean_search = search.gsub(/[*+\-><()~]/, '')
# 2. Add a single trailing wildcard for partial matching
ft_query = "#{clean_search}*"
# 3. Use the exact column list from your migration
# Using a hybrid approach to ensure "Jonh" still finds "John"
where(
"MATCH(name, phone_number, mobile_phone_number) AGAINST(? IN BOOLEAN MODE) OR
SOUNDEX(SUBSTRING_INDEX(name, ' ', 1)) = SOUNDEX(?) OR
name LIKE ?",
ft_query, clean_search, "%#{sanitize_sql_like(clean_search)}%"
).order(Arel.sql("MATCH(name, phone_number, mobile_phone_number) AGAINST(#{connection.quote(clean_search)}) DESC"))
end end
# Override the defult redmine seach method to rank results by id # Override the defult redmine seach method to rank results by id
@@ -208,14 +196,11 @@ class Customer < ActiveRecord::Base
scope = self.all scope = self.all
tokens.each do |token| tokens.each do |token|
q = "%#{sanitize_sql_like(token)}%" scope = scope.search(token)
scope = where("name LIKE ? OR phone_number LIKE ? OR mobile_phone_number LIKE ?", "%#{q}%", "%#{q}%", "%#{q}%")
end end
ids = scope.distinct.limit(options[:limit] || 100).pluck(:id) ids = scope.distinct.limit(options[:limit] || 100).pluck(:id)
ids.index_with { |id| id }
# Assign simple uniform ranking
ids.each_with_object({}) { |id, h| h[id] = id }
end end
# proforms a bruteforce sync operation # proforms a bruteforce sync operation
@@ -249,6 +234,30 @@ class Customer < ActiveRecord::Base
def to_s def to_s
return "#{self[:name]} - #{phone_number.split(//).last(4).join unless phone_number.nil?}" return "#{self[:name]} - #{phone_number.split(//).last(4).join unless phone_number.nil?}"
end end
# Push the updates
def save_with_push
begin
qbo = Qbo.first
@details = qbo.perform_authenticated_request do |access_token|
service = Quickbooks::Service::Customer.new(
company_id: qbo.realm_id,
access_token: access_token
)
service.update(@details)
end
self.id = @details.id
rescue => e
errors.add(:base, e.message)
return false
end
save_without_push
end
alias_method :save_without_push, :save
alias_method :save, :save_with_push
private private
@@ -265,24 +274,5 @@ class Customer < ActiveRecord::Base
@details = Quickbooks::Model::Customer.new @details = Quickbooks::Model::Customer.new
end end
end end
# Push the updates
def save_with_push
begin
qbo = Qbo.first
@details = qbo.perform_authenticated_request do |access_token|
service = Quickbooks::Service::Customer.new(company_id: qbo.realm_id, access_token: access_token)
service.update(@details)
end
#raise "QBO Fault" if @details.fault?
self.id = @details.id
rescue Exception => e
errors.add(e.message)
end
save_without_push
end
alias_method :save_without_push, :save
alias_method :save, :save_with_push
end end

View File

@@ -14,7 +14,7 @@ Redmine::Plugin.register :redmine_qbo do
name 'Redmine QBO plugin' name 'Redmine QBO plugin'
author 'Rick Barrette' author 'Rick Barrette'
description 'A pluging for Redmine to connect with QuickBooks Online to create Time Activity Entries for billable hours logged when an Issue is closed' description 'A pluging for Redmine to connect with QuickBooks Online to create Time Activity Entries for billable hours logged when an Issue is closed'
version '2026.2.14' version '2026.2.15'
url 'https://github.com/rickbarrette/redmine_qbo' url 'https://github.com/rickbarrette/redmine_qbo'
author_url 'https://barrettefabrication.com' author_url 'https://barrettefabrication.com'
settings default: {empty: true}, partial: 'qbo/settings' settings default: {empty: true}, partial: 'qbo/settings'