88 Commits
0.7.0 ... 0.8.0

Author SHA1 Message Date
086632e804 Version 0.8.0 (OAuth 2.0) 2020-01-04 16:01:16 -05:00
d37ff922fc Update customer.rb 2020-01-04 15:51:04 -05:00
3483efa100 Oauth2 Upgrade 2020-01-04 15:41:15 -05:00
f65eea2820 Oauth2 Upgrade 2020-01-04 15:37:17 -05:00
a4111e0a11 oAuth2 Upgrade 2020-01-04 15:36:36 -05:00
ebe5373d82 Oauth2 Upgrade 2020-01-04 15:34:57 -05:00
5b8c7d42c5 Oauth2 Upgrade 2020-01-04 15:34:04 -05:00
b8fc57d583 Oauth2 Upgrade 2020-01-04 15:31:19 -05:00
7c42197cb1 Oauth2 Upgrade 2020-01-04 15:30:03 -05:00
cc0ffce892 Oauth2 Upgrade 2020-01-04 15:29:18 -05:00
0fd2abbec3 Oauth2 Upgrade 2020-01-04 15:28:43 -05:00
215b219a6d Oauth2 Upgrade 2020-01-04 15:28:04 -05:00
ea71542d81 Oauth2 Upgrade 2020-01-04 15:26:35 -05:00
5dbf486b50 Update qbo_controller.rb 2020-01-04 15:21:14 -05:00
b734125d6b Update qbo_controller.rb 2020-01-04 15:19:31 -05:00
06e6295c6e Update qbo.rb 2020-01-04 14:32:32 -05:00
fd383ad9d4 Update qbo.rb 2020-01-04 14:29:23 -05:00
4eb6c533f1 Update qbo.rb 2020-01-04 14:21:07 -05:00
5af7d73768 Update qbo_controller.rb 2020-01-04 14:18:42 -05:00
1d0ae34261 Update qbo.rb 2020-01-04 14:13:29 -05:00
21656b3e14 Update qbo.rb 2020-01-04 14:09:37 -05:00
131976cd71 Update issue_patch.rb 2020-01-04 14:08:48 -05:00
88c1b9c9a2 Update issue_patch.rb 2020-01-04 14:06:42 -05:00
5ea9aed3cb Update qbo.rb 2020-01-04 14:05:00 -05:00
41e10d9b0e Update qbo.rb 2020-01-04 13:59:09 -05:00
45859bef3e Update qbo.rb 2020-01-04 13:56:38 -05:00
f5c40738dc Update issue_patch.rb 2020-01-04 13:54:29 -05:00
bfa37ee634 Update issue_patch.rb 2020-01-04 13:51:31 -05:00
787b55f3d7 Update qbo.rb 2020-01-04 13:46:13 -05:00
61f882e98c Update qbo.rb 2020-01-04 13:45:00 -05:00
37db0d3d72 Update qbo.rb 2020-01-04 13:31:40 -05:00
4f2dec3069 Update qbo.rb 2020-01-04 13:29:36 -05:00
35a7c3cfeb Create 030_update_qbos_token.rb 2020-01-04 13:28:22 -05:00
cbbaf5a95c Update qbo.rb 2020-01-04 13:22:27 -05:00
647923e5e6 Update 029_update_qbos_types.rb 2020-01-04 13:19:21 -05:00
70ca4e9964 Create 029_update_qbos_types.rb 2020-01-04 13:17:15 -05:00
7fb40ad4a8 Update qbo_controller.rb 2020-01-04 13:09:09 -05:00
36083d23a0 Update qbo_controller.rb 2020-01-04 13:07:36 -05:00
2ec57f2bbf Update qbo_controller.rb 2020-01-04 13:06:23 -05:00
278708e566 Update qbo_controller.rb 2020-01-04 12:47:30 -05:00
23f2b92e8d Update qbo_controller.rb 2020-01-04 12:45:16 -05:00
5d92eeddfb Update qbo_controller.rb 2020-01-04 12:44:05 -05:00
384a8c033c Update qbo_controller.rb 2020-01-04 12:39:23 -05:00
32b12b60f9 Update qbo_controller.rb 2020-01-04 12:33:54 -05:00
93db447239 Update qbo_controller.rb 2020-01-04 12:29:54 -05:00
19a6180e15 Update qbo.rb 2020-01-04 12:26:04 -05:00
3408ee173c Update qbo.rb 2020-01-04 12:25:12 -05:00
b817e842dd Update qbo.rb 2020-01-04 12:23:59 -05:00
51c3b8338e Update qbo.rb 2020-01-04 12:23:05 -05:00
c6a3edfbc1 Update qbo_controller.rb 2020-01-04 12:21:45 -05:00
21d8d90465 Update qbo.rb 2020-01-04 12:21:24 -05:00
04c0fa57c6 Update qbo_controller.rb 2020-01-04 12:17:26 -05:00
f5ad761712 Update qbo_controller.rb 2020-01-04 12:14:54 -05:00
9b80485915 Update qbo.rb 2020-01-04 12:14:05 -05:00
87de865c00 oauth2 2020-01-04 11:51:25 -05:00
1ea27e8511 Only show error flash if errors are not empty 2019-11-12 12:50:44 -05:00
8f0ca00b09 Merge branch 'master' of github.com:rickbarrette/redmine_qbo 2019-11-12 12:45:20 -05:00
859a1d505b always show errors 2019-11-12 12:45:14 -05:00
cd109653a2 Merge branch 'master' of github.com:rickbarrette/redmine_qbo 2019-11-12 12:37:12 -05:00
cab723bbcd Removed styles & removed after find call 2019-11-12 12:36:25 -05:00
3dd712629b added local vin validation 2019-11-12 12:35:54 -05:00
cdf2603e12 Merge branch 'master' of github.com:rickbarrette/redmine_qbo 2019-11-12 12:16:30 -05:00
5df9d324bc Update vehicle.rb 2019-11-12 12:15:59 -05:00
f78c0338b4 Made decode_vin not private 2019-11-12 12:08:04 -05:00
fe6aa7908f Merge branch 'master' of github.com:rickbarrette/redmine_qbo 2019-11-12 11:51:54 -05:00
aa45338e36 Update vehicle.rb 2019-11-11 10:10:23 -05:00
213dca2621 Fixed redicect to :home 2019-07-08 11:30:53 -04:00
fee710d717 Rediect to vehicle if vin is already taken 2019-06-26 11:39:07 -04:00
65eac58f6c Update customer.rb 2019-06-25 12:48:35 -04:00
b4f5112fc3 Create 028_add_customers_mobile_phone_number.rb 2019-06-25 12:46:03 -04:00
fa5dcbf9a9 Fixed redirect to for new vehicles 2019-06-25 12:37:49 -04:00
e0aebb1c23 Update customer.rb 2019-06-25 11:59:08 -04:00
6d176acc2b Update customer.rb 2019-06-25 11:54:26 -04:00
9e9b29fef9 Update 027_add_customers_phone_number.rb 2019-06-25 11:53:40 -04:00
1af846537d Update customer.rb 2019-06-25 11:50:14 -04:00
d6c5daff49 Added phone number 2019-06-25 11:15:57 -04:00
61c76ad80a Create 027_add_customers_phone_number.rb 2019-06-25 10:59:34 -04:00
0d514790fd Moved issue.status.is_closed? check back into issue save hook 2019-03-26 12:15:03 -04:00
748d431d35 Removed controller_issues_edit_before_save hook 2019-03-26 12:11:54 -04:00
87b8d99c41 Working on issue.bill_time 2019-03-26 12:09:38 -04:00
a0da53b6cf Fixed formatting & removed search from heading 2018-10-15 21:10:41 -04:00
02d630c631 Fixed Invoice Link to be HTML safe 2018-10-15 21:07:36 -04:00
15b214c800 Moved html into partial view 2018-10-15 20:57:03 -04:00
1b5e185087 Added nil check 2018-10-15 19:42:41 -04:00
102309600e Merge branch 'master' of github.com:rickbarrette/redmine_qbo 2018-10-15 19:36:09 -04:00
6acc7db91b Changed Customer search to shown on all pages 2018-10-15 19:34:51 -04:00
02898883a8 Update line_item.rb 2018-10-14 23:35:13 -04:00
ce02b70bc3 Update line_items_controler.rb 2018-10-14 23:34:17 -04:00
29 changed files with 302 additions and 209 deletions

View File

@@ -3,12 +3,11 @@ source 'https://rubygems.org'
gem 'quickbooks-ruby'
gem 'quickbooks-ruby-base'
gem 'oauth-plugin'
gem 'oauth'
gem 'oauth2'
gem 'roxml'
gem 'nhtsa_vin'
gem 'will_paginate'
gem 'rails-jquery-autocomplete'
gem 'jquery-rails', '~> 3.1.4'
gem 'jquery-ui-rails'
group :assets do

View File

@@ -77,7 +77,7 @@ Note: After the inital synchronization, this plugin will recieve push notificati
The MIT License (MIT)
Copyright (c) 2017 rick barrette
Copyright (c) 2020 rick barrette
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@@ -1,10 +1,8 @@
#The License
#The MIT License (MIT)
#
#Copyright (c) 2018 Rick Barrette - All Rights Reserved
#
#Unauthorized copying of this software and associated documentation files (the "Software"), via any medium is strictly prohibited.
#Copyright (c) 2018 rick barrette
#
#Proprietary and confidential
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
#

View File

@@ -19,9 +19,9 @@ class PaymentsController < ApplicationController
@customers = Customer.all.sort_by &:name
@accounts = Qbo.get_base(:account).service.query("SELECT Id, Name FROM Account WHERE AccountType = 'Bank' Order By Name")
@accounts = Qbo.get_base(:account).query("SELECT Id, Name FROM Account WHERE AccountType = 'Bank' Order By Name")
@payment_methods = Qbo.get_base(:payment_method).service.all
@payment_methods = Qbo.get_base(:payment_method).all
end
def create

View File

@@ -34,32 +34,48 @@ class QboController < ApplicationController
# Called when the user requests that Redmine to connect to QBO
#
def authenticate
callback = qbo_oauth_callback_url
token = Qbo.get_oauth_consumer.get_request_token(:oauth_callback => callback)
session[:qb_request_token] = Marshal.dump(token)
redirect_to("https://appcenter.intuit.com/Connect/Begin?oauth_token=#{token.token}") and return
oauth2_client = Qbo.get_client
callback = "https://redmine.rickbarrette.org/qbo/oauth_callback/"
#callback = qbo_oauth_callback_url
grant_url = oauth2_client.auth_code.authorize_url(redirect_uri: callback, response_type: "code", state: SecureRandom.hex(12), scope: "com.intuit.quickbooks.accounting")
redirect_to grant_url
end
#
# Called by QBO after authentication has been processed
#
def oauth_callback
at = Marshal.load(session[:qb_request_token]).get_access_token(:oauth_verifier => params[:oauth_verifier])
#There can only be one...
Qbo.destroy_all
# Save the authentication information
qbo = Qbo.new
qbo.qb_token = at.token
qbo.qb_secret = at.secret
qbo.token_expires_at = 6.months.from_now.utc
qbo.reconnect_token_at = 5.months.from_now.utc
qbo.company_id = params['realmId']
if qbo.save!
redirect_to qbo_sync_path, :flash => { :notice => "Successfully connected to Quickbooks" }
else
redirect_to plugin_settings_path(:redmine_qbo), :flash => { :error => "Error" }
if params[:state].present?
oauth2_client = Qbo.get_client
# use the state value to retrieve from your backend any information you need to identify the customer in your system
#redirect_uri = qbo_oauth_callback_url
redirect_uri = "https://redmine.rickbarrette.org/qbo/oauth_callback/"
if resp = oauth2_client.auth_code.get_token(params[:code], redirect_uri: redirect_uri)
# save your tokens here. For example:
# quickbooks_credentials.update_attributes(access_token: resp.token, refresh_token: resp.refresh_token,
# realm_id: params[:realmId])
Qbo.delete_all
# Save the authentication information
qbo = Qbo.new
qbo.qb_token = resp.token
qbo.qb_secret = resp.refresh_token
qbo.token_expires_at = 6.months.from_now.utc
qbo.reconnect_token_at = 3.months.from_now.utc
qbo.company_id = params[:realmId]
access_token = OAuth2::AccessToken.new(oauth2_client, resp.token, refresh_token: resp.refresh_token)
qbo.token = access_token
qbo.expire = 1.hour.from_now.utc
if qbo.save!
redirect_to qbo_sync_path, :flash => { :notice => "Successfully connected to Quickbooks" }
else
redirect_to plugin_settings_path(:redmine_qbo), :flash => { :error => "Error" }
end
end
end
end
@@ -144,6 +160,6 @@ class QboController < ApplicationController
ActiveRecord::Base.connection.close
end
redirect_to :back, :flash => { :notice => "Successfully synced to Quickbooks" }
redirect_to :home, :flash => { :notice => "Successfully synced to Quickbooks" }
end
end

View File

@@ -48,7 +48,7 @@ class VehiclesController < ApplicationController
redirect_to @vehicle
else
flash[:error] = @vehicle.errors.full_messages.to_sentence
redirect_to new_vehicle_path
redirect_to Vehicle.find_by_vin @vehicle.vin
end
end
@@ -81,9 +81,10 @@ class VehiclesController < ApplicationController
flash[:notice] = "Vehicle updated"
redirect_to @vehicle
else
flash[:error] = @vehicle.errors.full_messages.to_sentence if @vehicle.errors
redirect_to edit_vehicle_path
end
#show any errors anyways
flash[:error] = @vehicle.errors.full_messages.to_sentence unless @vehicle.errors.empty?
rescue ActiveRecord::RecordNotFound
render_404
end

View File

@@ -1,6 +1,6 @@
#The MIT License (MIT)
#
#Copyright (c) 2017 rick barrette
#Copyright (c) 2020 rick barrette
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
@@ -17,7 +17,7 @@ class Customer < ActiveRecord::Base
has_many :qbo_estimates
has_many :vehicles
attr_accessible :name, :notes, :email, :primary_phone, :mobile_phone
attr_accessible :name, :notes, :email, :primary_phone, :mobile_phone, :phone_number
validates_presence_of :id, :name
self.primary_key = :id
@@ -63,6 +63,8 @@ class Customer < ActiveRecord::Base
pn = Quickbooks::Model::TelephoneNumber.new
pn.free_form_number = n
@details.primary_phone = pn
#update our locally stored number too
update_phone_number
end
# Convenience Method
@@ -83,6 +85,26 @@ class Customer < ActiveRecord::Base
pn = Quickbooks::Model::TelephoneNumber.new
pn.free_form_number = n
@details.mobile_phone = pn
#update our locally stored number too
update_mobile_phone_number
end
# update the localy stored phone number as a plain string with no special chars
def update_phone_number
begin
self.phone_number = self.primary_phone.tr('^0-9', '')
rescue
return nil
end
end
# update the localy stored phone number as a plain string with no special chars
def update_mobile_phone_number
begin
self.mobile_phone_number = self.mobile_phone.tr('^0-9', '')
rescue
return nil
end
end
# Convenience Method
@@ -114,7 +136,7 @@ class Customer < ActiveRecord::Base
# proforms a bruteforce sync operation
# This needs to be simplified
def self.sync
service = Qbo.get_base(:customer).service
service = Qbo.get_base(:customer)
# Sync ALL customers if the database is empty
#if count == 0
@@ -142,26 +164,16 @@ class Customer < ActiveRecord::Base
end
end
# Searchs the database for a customer by name
# Searchs the database for a customer by name or phone number with out special chars
def self.search(search)
customers = where("name LIKE ?", "%#{search}%")
#if customers.empty?
# service = Qbo.get_base(:customer).service
# results = service.query("Select Id From Customer Where PrimaryPhone LIKE '%#{search}%' AND Mobile LIKE '%#{search}%'")
# results.each do |customer|
# customers << Customer.find_by_id(customer.id)
# end
#end
customers = where("name LIKE ? OR phone_number LIKE ? OR mobile_phone_number LIKE ?", "%#{search}%", "%#{search}%", "%#{search}%")
return customers.order(:name)
end
# proforms a bruteforce sync operation
# This needs to be simplified
def self.sync_by_id(id)
service = Qbo.get_base(:customer).service
service = Qbo.get_base(:customer)
customer = service.fetch_by_id(id)
qbo_customer = Customer.find_or_create_by(id: customer.id)
@@ -181,7 +193,7 @@ class Customer < ActiveRecord::Base
# Push the updates
def save_with_push
begin
@details = Qbo.get_base(:customer).service.update(@details)
@details = Qbo.get_base(:customer).update(@details)
#raise "QBO Fault" if @details.fault?
self.id = @details.id
rescue Exception => e
@@ -199,7 +211,7 @@ class Customer < ActiveRecord::Base
def pull
begin
raise Exception unless self.id
@details = Qbo.get_base(:customer).find_by_id(self.id)
@details = Qbo.get_base(:customer).fetch_by_id(self.id)
rescue Exception => e
@details = Quickbooks::Model::Customer.new
end

View File

@@ -1,8 +1,8 @@
#Copyright (c) 2018 Rick Barrette - All Rights Reserved
#
#Unauthorized copying of this software and associated documentation files (the "Software"), via any medium is strictly prohibited.
#The MIT License (MIT)
#
#Proprietary and confidential
#Copyright (c) 2018 rick barrette
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
#

View File

@@ -1,6 +1,6 @@
#The MIT License (MIT)
#
#Copyright (c) 2017 rick barrette
#Copyright (c) 2020 rick barrette
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
@@ -23,7 +23,7 @@ class Payment
payment.deposit_to_account_id = @account_id.to_i
payment.payment_method_id = @payment_method_id.to_i
payment.total = @total_amount
Qbo.get_base(:payment).service.update(payment)
Qbo.get_base(:payment).update(payment)
end
def save!

View File

@@ -1,6 +1,6 @@
#The MIT License (MIT)
#
#Copyright (c) 2017 rick barrette
#Copyright (c) 2020 rick barrette
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
@@ -15,29 +15,53 @@ class Qbo < ActiveRecord::Base
OAUTH_CONSUMER_KEY = Setting.plugin_redmine_qbo['settingsOAuthConsumerKey']
OAUTH_CONSUMER_SECRET = Setting.plugin_redmine_qbo['settingsOAuthConsumerSecret']
$qb_oauth_consumer = OAuth::Consumer.new(OAUTH_CONSUMER_KEY, OAUTH_CONSUMER_SECRET, {
:site => "https://oauth.intuit.com",
:request_token_path => "/oauth/v1/get_request_token",
:authorize_url => "https://appcenter.intuit.com/Connect/Begin",
:access_token_path => "/oauth/v1/get_access_token"
})
# Configure quickbooks-ruby-base to access our database
Quickbooks::Base.configure do |c|
c.persistent_token = 'qb_token'
c.persistent_secret = 'qb_secret'
c.persistent_company_id = 'company_id'
def self.get_client
oauth_params = {
site: "https://appcenter.intuit.com/connect/oauth2",
authorize_url: "https://appcenter.intuit.com/connect/oauth2",
token_url: "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer"
}
return OAuth2::Client.new(OAUTH_CONSUMER_KEY, OAUTH_CONSUMER_SECRET, oauth_params)
end
def self.get_oauth_consumer
# Quickbooks Config Info
return $qb_oauth_consumer
end
# Get a quickbooks base object for type
# Get a quickbooks base service object for type
# @params type of base
def self.get_base(type)
Quickbooks::Base.new(first, type)
oauth2_client = get_client
qbo = self.first
access_token = OAuth2::AccessToken.new(oauth2_client, qbo.qb_token , refresh_token: qbo.qb_secret)
# check to see if we need to refest the token
#if qbo.expire.to_date.past?
# new_access_token_object = access_token.refresh!
# qbo.token = new_access_token_object
# qbo.expire = 1.hour.from_now.utc
# qbo.save!
# access_token = new_access_token_object
#end
case type
when :item
return Quickbooks::Service::Item.new(:company_id => qbo.company_id, :access_token => access_token)
when :time_activity
return Quickbooks::Service::TimeActivity.new(:company_id => qbo.company_id, :access_token => access_token)
when :customer
return Quickbooks::Service::Customer.new(:company_id => qbo.company_id, :access_token => access_token)
when :invoice
return Quickbooks::Service::Invoice.new(:company_id => qbo.company_id, :access_token => access_token)
when :estimate
return Quickbooks::Service::Estimate.new(:company_id => qbo.company_id, :access_token => access_token)
when :account
return Quickbooks::Service::Account.new(:company_id => qbo.company_id, :access_token => access_token)
else
return access_token
end
end
# Get the QBO account

View File

@@ -1,6 +1,6 @@
#The MIT License (MIT)
#
#Copyright (c) 2017 rick barrette
#Copyright (c) 2020 rick barrette
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
@@ -19,7 +19,7 @@ class QboEmployee < ActiveRecord::Base
end
def self.sync
employees = get_base.service.all
employees = get_base.all
transaction do
# Update the item table
@@ -33,7 +33,7 @@ class QboEmployee < ActiveRecord::Base
end
def self.sync_by_id(id)
employee = get_base.service.fetch_by_id(id)
employee = get_base.fetch_by_id(id)
qbo_employee = find_or_create_by(id: employee.id)
qbo_employee.name = employee.display_name
qbo_employee.id = employee.id

View File

@@ -1,6 +1,6 @@
#The MIT License (MIT)
#
#Copyright (c) 2017 rick barrette
#Copyright (c) 2020 rick barrette
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
@@ -19,7 +19,7 @@ class QboEstimate < ActiveRecord::Base
# return the QBO Estimate service
def self.get_base
Qbo.get_base(:estimate).service
Qbo.get_base(:estimate)
end
# sync all estimates

View File

@@ -1,6 +1,6 @@
#The MIT License (MIT)
#
#Copyright (c) 2017 rick barrette
#Copyright (c) 2020 rick barrette
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
@@ -18,7 +18,7 @@ class QboInvoice < ActiveRecord::Base
self.primary_key = :id
def self.get_base
Qbo.get_base(:invoice).service
Qbo.get_base(:invoice)
end
# sync ALL the invoices

View File

@@ -1,6 +1,6 @@
#The MIT License (MIT)
#
#Copyright (c) 2017 rick barrette
#Copyright (c) 2020 rick barrette
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
@@ -27,9 +27,9 @@ class QboItem < ActiveRecord::Base
query << " AND Metadata.LastUpdatedTime >= '#{last.iso8601}' " if last
if count == 0
items = get_base.service.all
items = get_base.all
else
items = get_base.service.query(query)
items = get_base.query(query)
end
unless items.count = 0

View File

@@ -1,6 +1,6 @@
#The MIT License (MIT)
#
#Copyright (c) 2017 rick barrette
#Copyright (c) 2020 rick barrette
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
@@ -20,11 +20,11 @@ class QboPurchase < ActiveRecord::Base
end
def get_purchase(id)
get_base.service.find_by_id(id)
get_base.find_by_id(id)
end
def self.sync
QboPurchase.get_base.service.all.each { |purchase|
QboPurchase.get_base.all.each { |purchase|
purchase.line_items.all? { |line_item|

View File

@@ -20,7 +20,7 @@ class Vehicle < ActiveRecord::Base
validates_presence_of :customer
validates :vin, uniqueness: true
before_save :decode_vin
after_initialize :get_details
#after_find :get_details
self.primary_key = :id
@@ -36,11 +36,13 @@ class Vehicle < ActiveRecord::Base
# returns the raw JSON details from EMUNDS
def details
get_details if @details.nil?
return @details
end
# returns the style of the vehicle
def style
get_details if @details.nil?
begin
return @details.trim if @details
rescue
@@ -57,16 +59,17 @@ class Vehicle < ActiveRecord::Base
# returns the number of doors of the vehicle
def doors
get_details if @details.nil?
return @details.doors if @details
end
# Force Upper Case for VIN numbers
# Force Upper Case for make numbers
def make=(val)
# The to_s is in case you get nil/non-string
write_attribute(:make, val.to_s.titleize)
end
# Force Upper Case for VIN numbers
# Force Upper Case for model numbers
def model=(val)
# The to_s is in case you get nil/non-string
write_attribute(:model, val.to_s.titleize)
@@ -83,22 +86,7 @@ class Vehicle < ActiveRecord::Base
def self.search(search)
where("vin LIKE ?", "%#{search}%")
end
private
# init method to pull JSON details from Edmunds
def get_details
if self.vin?
begin
query = NhtsaVin.get(self.vin)
raise RuntimeError, query.error unless query.valid?
@details = query.response
rescue Exception => e
errors.add(:vin, e.message)
end
end
end
# decodes a vin and updates self
def decode_vin
get_details
@@ -114,4 +102,24 @@ private
self.name = to_s
end
private
# init method to pull JSON details from Edmunds
def get_details
if self.vin?
#validate the vin before calling a remote server
validation = NhtsaVin.validate(self.vin)
begin
#if the vin validation failed, raise an exception and exit
raise RuntimeError, validation.error unless validation.valid?
# query NHTSA for details on the vin
query = NhtsaVin.get(self.vin)
raise RuntimeError, query.error unless query.valid?
@details = query.response
rescue Exception => e
errors.add(:vin, e.message)
end
end
end
end

View File

@@ -1,5 +1,4 @@
<div id="content">
<h2>Customer #<%= @customer.id %> - <%= @customer.name %> <span style="float:right"> <%= render :partial => 'customers/search' %> </span> </h2>
<h2>Customer #<%= @customer.id %> - <%= @customer.name %> </h2>
<br/>
<div class="subject">
@@ -23,4 +22,3 @@
<h2>Issues:</h2>
<%= render :partial => 'issues/list_simple', locals: {issues: @issues} %>
</div>
</div>

View File

@@ -0,0 +1,35 @@
<div class="splitcontent">
<div class="splitcontentleft">
<div class="customer_id attribute">
<div class="label"><span>Customer</span>:</div>
<div class="value"><%= customer %></div>
</div>
<div class="qbo_estimate_id attribute">
<div class="label"><span>Estimate</span>:</div>
<div class="value"><%= estimate_link %></div>
</div>
<div class="qbo_invoice_id attribute">
<div class="label"><span>Invoice</span>:</div>
<div class="value"><%= invoice_link %></div>
</div>
</div>
<div class="splitcontentleft">
<div class="vehicle attribute">
<div class="label"><span>Vehicle</span>:</div>
<div class="value"><%= vehicle %></div>
</div>
<div class="vehicle_vin attribute">
<div class="label"><span>VIN</span>:</div>
<div class="value"><%=split_vin[0] if split_vin%><b><%=split_vin[1] if split_vin%></b></div>
</div>
<div class="vehicle_notes attribute">
<div class="label"><span>Notes</span>:</div>
<div class="value"><%=notes%></div>
</div>
</div>
</div>

View File

@@ -16,11 +16,6 @@
<td><%= @vin[0] if @vin %><b><%=@vin[1] if @vin%></b></td>
</tr>
<tr>
<th>Style</th>
<td><%= vehicle.style %></td>
</tr>
<tr>
<th>Notes</th>
<td><%= vehicle.notes %></td>

View File

@@ -0,0 +1,15 @@
#The MIT License (MIT)
#
#Copyright (c) 2019 rick barrette
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
class AddCustomersPhoneNumber < ActiveRecord::Migration
def change
add_column :customers, :phone_number, :string
end
end

View File

@@ -0,0 +1,15 @@
#The MIT License (MIT)
#
#Copyright (c) 2019 rick barrette
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
class AddCustomersMobilePhoneNumber < ActiveRecord::Migration
def change
add_column :customers, :mobile_phone_number, :string
end
end

View File

@@ -0,0 +1,17 @@
#The MIT License (MIT)
#
#Copyright (c) 2020 rick barrette
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
class UpdateQbosTypes < ActiveRecord::Migration
def change
change_column :qbos, :qb_token, :text
change_column :qbos, :qb_secret, :text
end
end

View File

@@ -0,0 +1,17 @@
#The MIT License (MIT)
#
#Copyright (c) 2020 rick barrette
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
class UpdateQbosToken < ActiveRecord::Migration
def change
add_column :qbos, :token, :text
add_column :qbos, :expire, :date
end
end

View File

@@ -1,6 +1,6 @@
#The MIT License (MIT)
#
#Copyright (c) 2017 rick barrette
#Copyright (c) 2020 rick barrette
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
@@ -31,7 +31,7 @@ Redmine::Plugin.register :redmine_qbo do
name 'Redmine Quickbooks Online plugin'
author 'Rick Barrette'
description 'This is a plugin for Redmine to intergrate with Quickbooks Online to allow for seamless intergration CRM and invoicing of completed issues'
version '0.7.0'
version '0.8.0'
url 'https://github.com/rickbarrette/redmine_qbo'
author_url 'http://rickbarrette.org'
settings :default => {'empty' => true}, :partial => 'qbo/settings'

View File

@@ -1,6 +1,6 @@
#The MIT License (MIT)
#
#Copyright (c) 2017 rick barrette
#Copyright (c) 2020 rick barrette
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
@@ -42,38 +42,46 @@ module IssuePatch
# Create billable time entries
def bill_time
# Check to see if we have everything we need to bill the customer
#return unless status.is_closed?
return if assigned_to.nil?
return unless Qbo.first
return unless customer
# Get unbilled time entries
spent_time = time_entries.where(qbo_billed: [false, nil])
spent_hours ||= spent_time.sum(:hours) || 0
if spent_hours > 0 then
# Prepare to create a new Time Activity
time_service = Qbo.get_base(:time_activity).service
item_service = Qbo.get_base(:item).service
time_service = Qbo.get_base(:time_activity)
item_service = Qbo.get_base(:item)
time_entry = Quickbooks::Model::TimeActivity.new
# Lets total up each activity before billing.
# This will simpify the invoicing with a single billable time entry per time activity
h = Hash.new(0)
spent_time.each do |entry|
# Lets tottal up each activity
h[entry.activity.name] += entry.hours
# update time entries billed status
entry.qbo_billed = true
entry.save
end
# Now letes upload our totals for each activity as their own billable time entry
h.each do |key, val|
# Convert float spent time to hours and minutes
hours = val.to_i
minutesDecimal = (( val - hours) * 60)
minutes = minutesDecimal.to_i
# Lets match the activity to an qbo item
item = item_service.query("SELECT * FROM Item WHERE Name = '#{key}' ").first
next if item.nil?
# Create the new billable time entry and upload it
time_entry.description = "#{tracker} ##{id}: #{subject} #{"(Partial @ #{done_ratio}%)" if not closed?}"
# TODO entry.user.qbo_employee.id
time_entry.employee_id = assigned_to.qbo_employee_id

View File

@@ -23,8 +23,10 @@ class IssuesFormHookListener < Redmine::Hook::ViewListener
f = context[:form]
#check project level customer/vehicle ownership first
selected_customer = context[:project].customer ? context[:project].customer.id : nil
selected_vehicle = context[:project].vehicle ? context[:project].vehicle.id : nil
if context[:project]
selected_customer = context[:project].customer ? context[:project].customer.id : nil
selected_vehicle = context[:project].vehicle ? context[:project].vehicle.id : nil
end
# Check to see if there is a quickbooks user attached to the issue
selected_customer = context[:issue].customer ? context[:issue].customer.id : nil

View File

@@ -10,47 +10,10 @@
class IssuesSaveHookListener < Redmine::Hook::ViewListener
#Before Issue Saved
def controller_issues_edit_before_save(context={})
issue = context[:issue]
# Check to see if we have registered with QBO
if Qbo.first && issue.customer && issue. qbo_item_id
# if this is a quote, lets create a new estimate based off estimated hours
if issue.tracker.name = "Quote" && issue.status.name = "New" && issue.qbo_estimate
# Get QBO Services
item_service = QboItem.get_base.service
estimate_base = QboEstimate.get_base
# Create the estimate
estimate = estimate_base.qr_model(:estimate)
estimate.customer_id = issue.customer_id
estimate.txn_date = Date.today
# Create the line item for labor
item = item_service.fetch_by_id(issue.qbo_item_id)
line_item = Quickbooks::Model::InvoiceLineItem.new
line_item.amount = item.unit_price * issue.estimated_hours
line_item.description = issue.subject
line_item.sales_item! do |detail|
detail.unit_price = item.unit_price
detail.quantity = issue.estimated_hours
detail.item_id = issue.qbo_item_id
end
# Add the line items to the estimate
estimate.line_items << line_item
end
end
end
# Called After Issue Saved
# Called After Issue Saved
def controller_issues_edit_after_save(context={})
issue = context[:issue]
issue.bill_time if Qbo.first && issue.customer && issue.status.is_closed?
issue.bill_time if issue.status.is_closed?
end
end

View File

@@ -10,11 +10,6 @@
class IssuesShowHookListener < Redmine::Hook::ViewListener
# Additional context fields
# :issue => the issue this is edited
# :f => the form object to create additional fields
#render_on :view_issues_show_details_bottom, :partial => 'hooks/redmine_qbo/_view_issues_show_details_bottom.html.erb'
# View Issue
# Display the quickbooks contact in the issue
def view_issues_show_details_bottom(context={})
@@ -37,6 +32,7 @@ class IssuesShowHookListener < Redmine::Hook::ViewListener
issue.qbo_invoice_ids.each do |i|
invoice = QboInvoice.find i
invoice_link = invoice_link + link_to( invoice.doc_number, "#{Redmine::Utils::relative_url_root}/qbo/invoice/#{i}", :target => "_blank").to_s + " "
invoice_link = invoice_link.html_safe
end
end
@@ -50,46 +46,20 @@ class IssuesShowHookListener < Redmine::Hook::ViewListener
end
split_vin = vin.scan(/.{1,9}/) if vin
return "
<div class=\"splitcontent\">
<div class=\"splitcontentleft\">
<div class=\"customer_id attribute\">
<div class=\"label\"><span>Customer</span>:</div>
<div class=\"value\">#{customer}</div>
</div>
<div class=\"qbo_estimate_id attribute\">
<div class=\"label\"><span>Estimate</span>:</div>
<div class=\"value\">#{estimate_link}</div>
</div>
<div class=\"qbo_invoice_id attribute\">
<div class=\"label\"><span>Invoice</span>:</div>
<div class=\"value\">#{invoice_link}</div>
</div>
</div>
<div class=\"splitcontentleft\">
<div class=\"vehicle attribute\">
<div class=\"label\"><span>Vehicle</span>:</div>
<div class=\"value\">#{vehicle}</div>
</div>
<div class=\"vehicle_vin attribute\">
<div class=\"label\"><span>VIN</span>:</div>
<div class=\"value\">#{split_vin[0] if split_vin}<b>#{split_vin[1] if split_vin}</b></div>
</div>
<div class=\"vehicle_notes attribute\">
<div class=\"label\"><span>Notes</span>:</div>
<div class=\"value\">#{notes}</div>
</div>
</div>
</div>"
context[:controller].send(:render_to_string, {
:partial => 'qbo/issues_show_details',
locals: {
customer: customer,
estimate_link: estimate_link,
invoice_link: invoice_link,
vehicle: vehicle,
split_vin: split_vin,
notes: notes
}
})
end
def view_issues_show_description_bottom(context={})
bill_button = button_to "Bill Time", "#{Redmine::Utils::relative_url_root}/qbo/bill/#{context[:issue].id}", method: :get if User.current.admin?
share_button = button_to "Share", "#{Redmine::Utils::relative_url_root}/customers/view/#{context[:issue].share_token.token}", method: :get if User.current.logged?

View File

@@ -1,3 +1,3 @@
class ViewHookListener < Redmine::Hook::ViewListener
render_on :view_issues_sidebar_issues_bottom, :partial => "customers/sidebar"
render_on :view_layouts_base_sidebar, :partial => "customers/sidebar"
end