115 Commits
0.0.8 ... 0.2.0

Author SHA1 Message Date
eb039368bb Version bump 0.2.0
* New Top & Application Menus
* Move quickbooks stats into QBO settings page
* Fixed New vehicle for customer link
* Added issue linking from hashtag numbers in QBO invoice line items
* Cleaned up customer & vehicle lists
* Implemented delete calls from web hook
* Added webhook token verification
* More...
2016-08-04 10:12:12 -04:00
0dea5917a7 Update _settings.html.erb 2016-08-04 10:06:07 -04:00
a8ccde6c81 Update _settings.html.erb 2016-08-04 10:05:07 -04:00
787ae1b8df Update _settings.html.erb 2016-08-04 10:03:28 -04:00
276c89d4ac Update init.rb 2016-08-04 09:59:12 -04:00
9a395ee25c Update _list.html.erb 2016-08-04 09:53:02 -04:00
475c86eabe Update show.html.erb 2016-08-04 09:52:32 -04:00
259737a488 Update show.html.erb 2016-08-04 09:51:47 -04:00
362cb77381 Update show.html.erb 2016-08-04 09:50:55 -04:00
8cfab17136 Update show.html.erb 2016-08-04 09:50:37 -04:00
f0018ab87d Update _details.html.erb 2016-08-04 09:49:52 -04:00
8f87eb3e60 Update show.html.erb 2016-08-04 09:48:19 -04:00
2b093903b3 Update show.html.erb 2016-08-04 09:46:22 -04:00
05017dcc4f Update show.html.erb 2016-08-04 09:45:39 -04:00
0e9b5fa17a Update show.html.erb 2016-08-04 09:44:09 -04:00
dd335aff71 Update _form.html.erb 2016-08-04 00:18:32 -04:00
0f61bf54ce Update index.html.erb 2016-08-04 00:15:33 -04:00
14cb22d743 Update vehicles_controller.rb 2016-08-04 00:13:47 -04:00
702ab5013e Update index.html.erb 2016-08-04 00:11:54 -04:00
235e2c6e7b Update routes.rb 2016-08-04 00:10:46 -04:00
2e89a60d63 Update vehicles_controller.rb 2016-08-04 00:09:52 -04:00
3d5ef2cd8a Update vehicles_controller.rb 2016-08-04 00:08:21 -04:00
de8eff9bd2 Update vehicles_controller.rb 2016-08-04 00:03:35 -04:00
a9561d1694 Update vehicles_controller.rb 2016-08-04 00:02:56 -04:00
aa33de00d2 Update index.html.erb 2016-08-04 00:01:15 -04:00
ffc589fe80 Update index.html.erb 2016-08-03 23:59:50 -04:00
229e4e8d39 Update index.html.erb 2016-08-03 23:57:28 -04:00
d6dda2cdd6 Update init.rb 2016-08-03 23:54:17 -04:00
b8a101fddb Update routes.rb 2016-08-03 23:50:55 -04:00
c8a875b301 Update init.rb 2016-08-03 23:45:30 -04:00
df8e3a7465 Update init.rb 2016-08-03 23:35:43 -04:00
d91a6e3939 Update init.rb 2016-08-03 23:15:38 -04:00
48a2d683dd Update init.rb 2016-08-03 23:13:55 -04:00
44bf42c548 Update init.rb 2016-08-03 23:11:00 -04:00
d34e6cb0fd Update issues_form_hook_listener.rb 2016-08-03 23:05:19 -04:00
d8e7356ca3 Update init.rb 2016-08-03 23:03:59 -04:00
60e6dbaa6f Update issues_form_hook_listener.rb 2016-08-03 22:50:53 -04:00
47e5a7d0e4 Update issues_form_hook_listener.rb 2016-08-03 22:48:26 -04:00
9fa2165907 Update issues_form_hook_listener.rb 2016-08-03 22:46:42 -04:00
7385d7018c Update issues_form_hook_listener.rb 2016-08-03 22:45:23 -04:00
6124c1b307 Update issues_form_hook_listener.rb 2016-08-03 22:44:56 -04:00
b73535c6da Update issues_form_hook_listener.rb 2016-08-03 22:43:40 -04:00
1581023656 Update vehicles.js 2016-08-03 22:39:33 -04:00
0d21e2967d Rename vehicles.js.cof to vehicles.js 2016-08-03 22:37:48 -04:00
0dc7d83fbe Update issues_form_hook_listener.rb 2016-08-03 22:37:30 -04:00
cd18067384 Rename vehicles.js to vehicles.js.coffee 2016-08-03 22:36:12 -04:00
6c99f7095c Update issues_form_hook_listener.rb 2016-08-03 22:35:49 -04:00
eeaafce427 Rename vehicles.js.coffee to vehicles.js 2016-08-03 22:32:19 -04:00
b7cb27b5da Update vehicles.js.coffee 2016-08-03 22:30:04 -04:00
3e6286da7c Update vehicles.js.coffee 2016-08-03 22:28:31 -04:00
30ceea7fd5 Update vehicles_controller.rb 2016-08-03 22:23:07 -04:00
de9e973fd9 Update routes.rb 2016-08-03 22:22:23 -04:00
49a3bd5790 Update routes.rb 2016-08-03 22:19:23 -04:00
f1745930b1 Update routes.rb 2016-08-03 22:15:43 -04:00
d9beda8171 Rename update_vehicles.js.coffee.erb to update_vehicles.js.erb 2016-08-03 22:14:23 -04:00
65f343fb74 Update vehicles_controller.rb 2016-08-03 22:13:18 -04:00
892bd65fac Rename update_vehicles.js.coffee to update_vehicles.js.coffee.erb 2016-08-03 22:10:50 -04:00
0251191844 Update vehicles_controller.rb 2016-08-03 22:05:25 -04:00
65f6f52252 Update update_vehicles.js.coffee 2016-08-03 21:58:47 -04:00
4d94308bcc Update vehicles_controller.rb 2016-08-03 21:57:50 -04:00
7dcd8b24d2 Update vehicles.js.coffee 2016-08-03 21:55:31 -04:00
11da8e7a43 Update vehicles_controller.rb 2016-08-03 21:54:05 -04:00
56c895388d Update vehicles.js.coffee 2016-08-03 21:53:31 -04:00
8ec9567f15 Update issues_form_hook_listener.rb 2016-08-03 21:50:02 -04:00
be3dd0d131 Rename assets/javascripts/app/assets/vehicles.js.coffee to assets/javascripts/vehicles.js.coffee 2016-08-03 21:40:04 -04:00
92f51d9884 Create _vehicle.html.erb 2016-08-03 21:34:10 -04:00
c4904a0ac2 Create update_vehicles.js.coffee 2016-08-03 21:29:01 -04:00
0d87e5fb21 Create vehicles.js.coffee 2016-08-03 21:26:21 -04:00
d38e3e1702 Update routes.rb 2016-08-03 21:22:34 -04:00
fec59a7495 Update vehicles_controller.rb 2016-08-03 21:20:08 -04:00
a3b5ad0cb0 Update show.html.erb 2016-08-03 21:16:31 -04:00
bf21451819 Update show.html.erb 2016-08-03 21:15:40 -04:00
c6d3d9673b Update show.html.erb 2016-08-03 21:15:03 -04:00
3f5334a92d Update show.html.erb 2016-08-03 21:14:25 -04:00
bde7b83752 Update show.html.erb 2016-08-03 21:13:40 -04:00
c788e5724a Update show.html.erb 2016-08-03 21:12:54 -04:00
295cd12f9d Update show.html.erb 2016-08-03 21:11:57 -04:00
4a432481d9 Update show.html.erb 2016-08-03 21:11:05 -04:00
4a37d83694 Update show.html.erb 2016-08-03 21:09:15 -04:00
15a2a16379 Update show.html.erb 2016-08-03 21:08:42 -04:00
18fc7a6c8c Update vehicles_controller.rb 2016-08-03 21:02:45 -04:00
7aba8cdce3 Update qbo_invoice.rb 2016-08-03 16:40:05 -04:00
382e6675f1 Update qbo_invoice.rb 2016-08-03 16:39:07 -04:00
116d6896f4 Update qbo_invoice.rb 2016-08-03 16:38:09 -04:00
c9ced52112 Update qbo_invoice.rb 2016-08-03 16:36:50 -04:00
01b4bb4e53 Update qbo_invoice.rb 2016-08-03 16:35:04 -04:00
a266da2cd7 Update qbo_invoice.rb 2016-08-03 16:33:38 -04:00
578e7ba807 Update qbo_invoice.rb 2016-08-03 16:32:52 -04:00
b923e15d46 Update qbo_invoice.rb 2016-08-03 16:31:17 -04:00
1310d1e63e Update qbo_invoice.rb 2016-08-03 16:27:30 -04:00
a8e1e8429c Update qbo_invoice.rb 2016-08-03 16:25:33 -04:00
1b54b40f6c Update qbo_invoice.rb 2016-08-03 16:22:44 -04:00
6d7530922d Update qbo_invoice.rb 2016-08-03 16:20:26 -04:00
23698986b1 Update qbo_invoice.rb 2016-08-03 16:15:50 -04:00
1b4c377940 Update _details.html.erb 2016-08-01 22:35:12 -04:00
d33c0c9aa5 Update _details.html.erb 2016-08-01 22:34:38 -04:00
09d8c0024f Update _details.html.erb 2016-08-01 22:33:32 -04:00
06e827fff8 Version bump 0.1.0 2016-08-01 22:30:47 -04:00
b1844689df Update qbo_controller.rb 2016-08-01 22:29:53 -04:00
a4263a92ca Update issues_show_hook_listener.rb 2016-08-01 22:20:57 -04:00
14cc251809 Update Gemfile 2016-08-01 21:56:17 -04:00
471e8f3398 Update qbo_controller.rb 2016-08-01 21:56:00 -04:00
dadbda62c6 Update qbo_controller.rb 2016-08-01 21:55:30 -04:00
df47efe816 Update qbo_controller.rb 2016-08-01 21:54:31 -04:00
03cc6943a3 Update Gemfile 2016-08-01 21:51:04 -04:00
6f0163ce7d Update qbo_controller.rb 2016-08-01 21:49:17 -04:00
91110adad5 Update qbo_controller.rb 2016-08-01 21:47:30 -04:00
c2f48d0277 Update qbo_controller.rb 2016-08-01 21:41:47 -04:00
06344b6498 Update qbo_controller.rb 2016-08-01 21:34:14 -04:00
4ff2b2bdc6 Update _settings.html.erb 2016-08-01 21:28:15 -04:00
a71dd310fe Update issues_show_hook_listener.rb 2016-08-01 21:24:22 -04:00
90da7a5d74 Update issues_show_hook_listener.rb 2016-08-01 21:23:15 -04:00
6505f54c7f Update issues_show_hook_listener.rb 2016-08-01 21:22:31 -04:00
c4a488e5a7 Update issues_show_hook_listener.rb 2016-08-01 21:21:41 -04:00
71817f5ca8 Update issues_show_hook_listener.rb 2016-08-01 21:18:31 -04:00
16 changed files with 165 additions and 44 deletions

View File

@@ -11,6 +11,8 @@
class QboController < ApplicationController
unloadable
require 'openssl'
include AuthHelper
before_filter :require_user, :except => :qbo_webhook
@@ -64,41 +66,52 @@ class QboController < ApplicationController
# Quickbooks Webhook Callback
def qbo_webhook
if request.headers['Content-Type'] == 'application/json'
data = JSON.parse(request.body.read)
else
# application/x-www-form-urlencoded
data = params.as_json
end
# Process the information
entities = data['eventNotifications'][0]['dataChangeEvent']['entities']
entities.each do |entity|
id = entity['id'].to_i
name = entity['name']
# TODO rename all other models!
name.prepend("Qbo") if not name.eql? "Customer"
# Magicly initialize the correct class
obj = name.constantize
# for merge events
obj.delete(entity['deletedId']) if entity['deletedId']
#Check to see if we are deleting a record
if entity['operation'].eql? "Delete"
obj.delete(id)
#if not then update!
# check the payload
signature = request.headers['intuit-signature']
key = Setting.plugin_redmine_qbo['settingsWebhookToken']
data = request.body.read
hash = Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha256'), key, data)).strip()
# proceed if the request is good
if hash.eql? signature
if request.headers['content-type'] == 'application/json'
data = JSON.parse(data)
else
obj.sync_by_id(id)
# application/x-www-form-urlencoded
data = params.as_json
end
# Process the information
entities = data['eventNotifications'][0]['dataChangeEvent']['entities']
entities.each do |entity|
id = entity['id'].to_i
name = entity['name']
# TODO rename all other models!
name.prepend("Qbo") if not name.eql? "Customer"
# Magicly initialize the correct class
obj = name.constantize
# for merge events
obj.destroy(entity['deletedId']) if entity['deletedId']
#Check to see if we are deleting a record
if entity['operation'].eql? "Delete"
obj.destroy(id)
#if not then update!
else
obj.sync_by_id(id)
end
end
# Record that last time we updated
Qbo.update_time_stamp
# The webhook doesn't require a response but let's make sure we don't send anything
render :nothing => true
else
render nothing: true, status: 400
end
# Record that last time we updated
Qbo.update_time_stamp
# The webhook doesn't require a response but let's make sure we don't send anything
render :nothing => true
end
#

View File

@@ -18,6 +18,14 @@ class VehiclesController < ApplicationController
# display a list of all vehicles
def index
if params[:customer_id]
begin
@vehicles = Customer.find_by_id(params[:customer_id]).vehicles.paginate(:page => params[:page])
rescue ActiveRecord::RecordNotFound
render_404
end
end
if params[:search]
@vehicles = Vehicle.search(params[:search]).paginate(:page => params[:page])
if only_one_non_zero?(@vehicles)
@@ -30,6 +38,9 @@ class VehiclesController < ApplicationController
def new
@vehicle = Vehicle.new
@customers = Customer.all.order(:name)
if params[:customer]
@customer = Customer.find_by_id(params[:customer])
end
end
# create a new vehicle
@@ -92,6 +103,15 @@ class VehiclesController < ApplicationController
end
end
# returns a dynamic list of vehicles owned by a customer
def update_vehicles
@vehicles = Customer.find_by_id(params[:customer_id].to_i).vehicles
respond_to do |format|
format.html { render(:text => "not implemented") }
format.js
end
end
private
def only_one_non_zero?( array )

View File

@@ -46,11 +46,23 @@ class QboInvoice < ActiveRecord::Base
end
def self.sync_by_id(id)
#update the information in the database
invoice = get_base.service.fetch_by_id(id)
qbo_invoice = find_or_create_by(id: invoice.id)
qbo_invoice.doc_number = invoice.doc_number
qbo_invoice.id = invoice.id
qbo_invoice.save!
# Scan the line items for hashtags and attach to the applicable issues
invoice.line_items.each { |line|
if line.description
line.description.scan(/#(\w+)/).flatten.each { |issue|
i = Issue.find_by_id(issue.to_i)
i.qbo_invoice = QboInvoice.find_by_id(invoice.id.to_i)
i.save!
}
end
}
end
def self.update(id)

View File

@@ -30,8 +30,7 @@
<td><%= customer.issues.count %></td>
</tr>
<tr>
<td/>
<tr>
<td>
<%= button_to "Edit Customer", edit_customer_path(customer), method: :get%>
</td>

View File

@@ -1,7 +1,12 @@
<h1>Customer #<%= @customer.id %></h1>
<br/>
<h2>Details:</h2>
<%= render :partial => 'customers/details', locals: {customer: @customer} %>
<br/>
<h2>Vehicles:</h2>
<%= render :partial => 'vehicles/list' %>
<%= button_to "New Vehicle", new_customer_vehicle_path(@customer), method: :get %>
<br/>
<br/>
<h2>Issues:</h2>
<%= render :partial => 'issues/list_simple', locals: {issues: @issues} %>

View File

@@ -46,6 +46,15 @@ intuit.ipp.anywhere.setup({menuProxy: '/path/to/blue-dot', grantUrl: '<%= qbo_au
name="settings[settingsOAuthConsumerSecret]" >
</td>
</tr>
<tr>
<th>Intuit QBO Webhook Token</th>
<td>
<input type="text" style="width:350px" id="settingsWebhookToken"
value="<%= settings['settingsWebhookToken'] %>"
name="settings[settingsWebhookToken]" >
</td>
</tr>
<tr>
<th>Token Expires At</th>
@@ -63,6 +72,36 @@ intuit.ipp.anywhere.setup({menuProxy: '/path/to/blue-dot', grantUrl: '<%= qbo_au
<br/>
Note: You need to authenticate after saving your key and secret above
<br/>
<br/>
<!-- this will display a button that the user clicks to start the flow -->
<ipp:connectToIntuit></ipp:connectToIntuit>
<br/>
<br/>
<div>
<b>Customer Count:</b> <%= Customer.count%>
</div>
<div>
<b>Item Count:</b> <%= QboItem.count %>
</div>
<div>
<b>Employee Count:</b> <%= QboEmployee.count %>
</div>
<div>
<b>Invoice Count:</b> <%= QboInvoice.count %>
</div>
<div>
<b>Estimate Count:</b> <%= QboEstimate.count %>
</div>
<br/>
<div>
<b>Last Sync: </b> <%= Qbo.last_sync %> <%= link_to " Sync Now", qbo_sync_path %>
</div>

View File

@@ -3,7 +3,7 @@
<tr>
<th>Customer</th>
<td><%= vehicle.customer.name %></td>
<td><%= link_to vehicle.customer.name, customer_path(vehicle.customer) %></td>
</tr>
<tr>

View File

@@ -6,7 +6,7 @@
<div class="clearfix">
Customer:
<div class="input">
<%= f.collection_select :customer_id, @customers, :id, :name, include_blank: true, :selected => @customer, :required => true%>
<%= f.collection_select :customer_id, @customers, :id, :name, include_blank: true, :selected => params[:customer_id], :required => true%>
</div>
</div>

View File

@@ -16,8 +16,6 @@
</div>
<br/>
<% end %>
<br/>
<div class="actions">
<%= will_paginate @vehicles %>

View File

@@ -0,0 +1 @@
<option value="<%= vehicle.id %>"><%= vehicle.to_s.titleize %></option>

View File

@@ -0,0 +1 @@
$("#issue_vehicles_id").empty().append("<%= escape_javascript(render(:partial => @vehicles)) %>")

View File

@@ -0,0 +1,16 @@
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/
$ ->
$(document).on 'change', '#issue_customer_id', (evt) ->
$.ajax 'update_vehicles',
type: 'GET'
dataType: 'script'
data: {
customer_id: $("#issue_customer_id option:selected").val()
}
error: (jqXHR, textStatus, errorThrown) ->
console.log("AJAX Error: #{textStatus}")
success: (data, textStatus, jqXHR) ->
console.log("Dynamic vehicle select OK!")

View File

@@ -20,5 +20,11 @@ get 'qbo/invoice/:id', :to => 'invoice#show', as: :invoice
post 'qbo/webhook', :to => 'qbo#qbo_webhook'
#ajax
get "update_vehicles" => 'vehicles#update_vehicles', as: 'update_vehicles'
resources :customers do
resources :vehicles
end
resources :vehicles
resources :customers

12
init.rb
View File

@@ -25,7 +25,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.0.8'
version '0.2.0'
url 'https://github.com/rickbarrette/redmine_qbo'
author_url 'http://rickbarrette.org'
settings :default => {'empty' => true}, :partial => 'qbo/settings'
@@ -46,8 +46,12 @@ Redmine::Plugin.register :redmine_qbo do
WillPaginate.per_page = 10
# Register QBO top menu item
menu :top_menu, :qbo, { :controller => :qbo, :action => :index }, :caption => 'Quickbooks', :if => Proc.new { User.current.admin? }
menu :top_menu, :vehicles, { :controller => :vehicles, :action => :index }, :caption => 'Vehicles', :if => Proc.new { User.current.logged? }
#menu :top_menu, :qbo, { :controller => :qbo, :action => :index }, :caption => 'Quickbooks', :if => Proc.new { User.current.admin? }
menu :top_menu, :customers, { :controller => :customers, :action => :index }, :caption => 'Customers', :if => Proc.new { User.current.logged? }
menu :top_menu, :vehicles, { :controller => :vehicles, :action => :index }, :caption => 'Vehicles', :if => Proc.new { User.current.logged? }
menu :application_menu, :new_customer, { :controller => :customers, :action => :new }, :caption => 'New Customer', :if => Proc.new { User.current.logged? }
permission :customers, { :customers => [:index, :new] }, :public => false
menu :project_menu, :customers, { :controller => 'customers', :action => 'new' }, :caption => 'New Customer', :after => :activity, :param => :project_id
end

View File

@@ -10,6 +10,11 @@
class IssuesFormHookListener < Redmine::Hook::ViewListener
# Load the javascript
def view_layouts_base_html_head(context = {})
javascript_include_tag 'vehicles', :plugin => 'redmine_qbo'
end
# Edit Issue Form
# Show a dropdown for quickbooks contacts
def view_issues_form_details_bottom(context={})

View File

@@ -50,6 +50,8 @@ class IssuesShowHookListener < Redmine::Hook::ViewListener
#do nothing
end
split_vin = vin.scan(/.{1,9}/) if vin
return "
<div class=\"attributes\">
@@ -82,7 +84,7 @@ class IssuesShowHookListener < Redmine::Hook::ViewListener
<div class=\"vehicle_vin attribute\">
<div class=\"label\"><span>VIN</span>:</div>
<div class=\"value\">#{vin.gsub(/(.{9})/, '\1 ') if vin}</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\">