101 Commits
0.5.0 ... 0.7.0

Author SHA1 Message Date
d4d4a555f8 Forgot to add sidebar files 2018-10-14 23:32:07 -04:00
c2663cd0a0 License update 2018-10-14 23:31:17 -04:00
d48609361f Added customer search to sidebar 2018-10-14 23:27:19 -04:00
70995f6e55 Fixed formating 2018-10-04 20:59:46 -04:00
05a0472939 Changed redirect to :back 2018-10-04 20:57:24 -04:00
cff9f3fde3 Fixed new vehicle add missing customer 2018-09-22 00:29:54 -04:00
e24b704571 Added qbo partial views 2018-09-19 22:57:45 -04:00
4d99f54c79 Added autocomplete to vehicle customer field 2018-09-19 22:33:36 -04:00
e65725c334 Update init.rb 2018-09-19 10:15:13 -04:00
4829daab7c Update new.html.erb 2018-09-19 10:10:54 -04:00
260e9f3e4a Update _list.html.erb 2018-09-19 10:07:52 -04:00
e3ce2445b8 Update index.html.erb 2018-09-19 10:05:45 -04:00
2b333667ed Update init.rb 2018-09-19 09:52:58 -04:00
1077cf214c Update show.html.erb 2018-09-19 09:51:27 -04:00
f27fdf5274 Update index.html.erb 2018-09-19 09:50:58 -04:00
1dbcca4ca0 Create _search.html.erb 2018-09-19 09:49:51 -04:00
558e2359f7 Added partial customers/search & fixed formatting 2018-09-19 09:45:36 -04:00
f99ef648b3 Rename _search.erb to _search.html.erb 2018-09-19 09:24:05 -04:00
5e4e3329c8 Create _search.erb 2018-09-19 09:23:37 -04:00
b0a66aba0a Fixed custom field redering 2018-07-31 15:22:46 -04:00
f2dd500536 Merge branch 'line_items' of github.com:rickbarrette/redmine_qbo into line_items 2018-03-31 08:04:17 -04:00
7412ac4f91 Added boolean billed 2018-03-31 08:03:26 -04:00
2acb3efe5a Create line_items_controler.rb 2018-03-31 08:00:56 -04:00
2cc0d06bc5 Merge branch 'master' into line_items 2018-03-29 22:42:54 -04:00
4070cb7c49 Merge branch 'master' of github.com:rickbarrette/redmine_qbo 2018-03-29 22:42:03 -04:00
fcd196355a Create line_item.rb 2018-03-29 10:17:01 -04:00
ea502d5b7b Create 026_create_line_items.rb 2018-03-29 10:05:37 -04:00
1f33009f89 Update LICENSE 2018-03-28 15:24:44 -04:00
3509ae9725 Removed squish_vin and added last 8 of vin to vehicle name 2018-03-27 09:40:01 -04:00
49858c45c9 do a full search by setting the full parameter to true. 2018-03-27 09:38:44 -04:00
b78cd44cc9 don't bill time if not assigned to anyone EE 2018-03-05 08:58:33 -05:00
39fcd6d4dd Removed Drive & Doors 2018-03-03 13:55:22 -05:00
8838d36793 Fixed vin decoding 2018-03-03 13:54:19 -05:00
63fa94e6f2 Merge branch 'master' into nhtsa_vin 2018-03-03 13:14:05 -05:00
17183f9643 Update vehicle.rb 2018-03-03 13:11:58 -05:00
667d0bfa97 Remove Edmunds API & Added NhtsaVin 2018-03-03 12:38:56 -05:00
88a6be0d27 Remove Edmunds API Key Setting 2018-03-03 12:26:25 -05:00
c3eaddff97 Start work to switch from edmunds_vin to nhtsa_vin 2018-03-03 12:22:44 -05:00
f03adad463 Update issues_form_hook_listener.rb 2017-11-19 22:17:25 -05:00
bd03e3ac32 Did things 2017-11-19 22:14:40 -05:00
299a28a0d2 Create controller_issues_listener.rb 2017-11-19 22:01:20 -05:00
cee8ddced1 Added safe_attributes for ProjecT 2017-11-17 20:54:14 -05:00
738cd21b1f Update issues_form_hook_listener.rb 2017-11-17 10:56:13 -05:00
b8186e4b52 More customer/project relations 2017-11-17 10:51:25 -05:00
d98a8b8cc4 Fixed Project releationships and database migration 2017-11-17 10:14:05 -05:00
dba6c4b131 Update vehicle.rb 2017-11-13 22:19:20 -05:00
118812f16f Update vehicle.rb 2017-11-13 22:17:03 -05:00
0b96a1412c Rename app/views/qbo/list_simple.html.erb to app/views/issues/_list_simple.html.erb 2017-11-13 22:07:12 -05:00
29de191d26 Rename app/views/issues/list_simple.html.erb to app/views/qbo/list_simple.html.erb 2017-11-13 22:06:45 -05:00
f86af9ca71 Create list_simple.html.erb 2017-11-13 22:04:04 -05:00
d25de7b30f Merge remote-tracking branch 'origin/dev' 2017-11-13 21:37:28 -05:00
273bd3d6be Update application.js 2017-11-13 21:01:17 -05:00
ac446723f1 Update application.js 2017-11-13 20:59:45 -05:00
c21bc1333f Update projects_form_hook_listener.rb 2017-11-13 20:38:33 -05:00
4e4255995e Update projects_form_hook_listener.rb 2017-11-13 20:37:59 -05:00
c68b540597 Update projects_form_hook_listener.rb 2017-11-13 20:36:44 -05:00
1358871ccc Update init.rb 2017-11-13 20:31:16 -05:00
908511f299 Update init.rb 2017-11-13 20:30:28 -05:00
6260de21f9 Update query_patch.rb 2017-11-13 20:28:19 -05:00
e2f276097c Update query_patch.rb 2017-11-13 20:27:36 -05:00
205bb67a6a Update query_patch.rb 2017-11-13 20:20:46 -05:00
05edafec4c Update query_patch.rb 2017-11-13 20:13:26 -05:00
4a073d3a71 Update query_patch.rb 2017-11-13 20:12:23 -05:00
f2cbf31e17 Create projects_form_hook_listener.rb 2017-11-13 19:53:37 -05:00
22b22780ea Create project_patch.rb
Add relationships to projects
2017-11-13 19:47:39 -05:00
71cfa28817 Create 025_update_projects.rb
Added customer & vehicle reference to a project.
2017-11-13 19:40:14 -05:00
8b2d88f80b Create qbo_controller.rb 2017-06-14 09:43:52 -04:00
eaf0a57e51 Create qbo_controller.rb 2017-06-13 21:58:42 -04:00
512f5ad7ba Create index.html.erb 2017-06-13 21:55:13 -04:00
8d2351d3f9 Create vehicle.rb 2017-06-13 21:51:36 -04:00
c5a20c9e7f Create vehicle.rb 2017-06-13 21:50:50 -04:00
4a3b663333 Create vehicle.rb 2017-06-13 21:49:10 -04:00
e43635b5d8 Update vehicle.rb 2017-06-13 12:31:07 -04:00
7044377f16 Update vehicle.rb 2017-06-13 12:27:22 -04:00
7ced1bf942 Create vehicle.rb 2017-06-13 12:22:48 -04:00
7ca3315ce5 Update vehicle.rb 2017-06-13 11:44:46 -04:00
2b8c4b4d4d Update vehicle.rb 2017-06-06 08:58:11 -04:00
a359e8815b Update issues_form_hook_listener.rb 2017-06-06 08:53:36 -04:00
01cf82813c Update vehicle.rb 2017-06-06 08:32:21 -04:00
625e400c48 Update vehicle.rb 2017-06-06 08:27:55 -04:00
56793cee7c Update vehicle.rb 2017-06-06 08:25:35 -04:00
3ba5337812 Update vehicle.rb
Updated regex to remove invalid chars
2017-06-06 08:22:50 -04:00
129e3d4821 Update issues_form_hook_listener.rb 2017-06-06 08:12:08 -04:00
4d524a7d61 Update issues_form_hook_listener.rb 2017-06-06 08:10:28 -04:00
429fb920fb Update index.html.erb 2017-04-04 22:29:06 -04:00
77c7f0b6fe Update index.html.erb 2017-04-04 22:28:20 -04:00
1a043bea76 Added Permission Check 2017-04-04 22:26:58 -04:00
e4d770c272 Update index.html.erb 2017-04-04 22:23:33 -04:00
fce3931858 Added new customer button 2017-04-04 22:22:29 -04:00
43cdade6e1 Merge branch 'master' into dev 2017-04-04 09:32:50 -04:00
4374f9436c Removed .service from get_base call 2017-04-04 09:31:02 -04:00
7c63c3c816 Fixed typo 2017-04-04 08:49:08 -04:00
b3f491a60b Fixed logic 2017-04-04 08:48:36 -04:00
4adcbba840 Update vehicle.rb 2017-04-04 08:47:15 -04:00
baccb42455 Merge branch 'dev' of github.com:rickbarrette/redmine_qbo into dev 2017-04-04 08:46:44 -04:00
d0842dd803 Merge branch 'dev' of github.com:rickbarrette/redmine_qbo into dev 2017-04-04 08:45:45 -04:00
02aabe6045 Forgot End 2017-04-04 08:45:34 -04:00
0e47f9eb5f Some Cleanup & Fixed to_s to report vin
to_s to report vin when year,make,model are nil
2017-04-04 08:44:14 -04:00
f1d2d63f20 Removed un-needed initializer 2017-04-04 08:37:49 -04:00
f322f9f7ab Update Copyright 2017-04-04 08:34:03 -04:00
6db8b76902 Update Copyright 2017-04-04 08:33:37 -04:00
39 changed files with 380 additions and 112 deletions

View File

@@ -5,7 +5,7 @@ gem 'quickbooks-ruby-base'
gem 'oauth-plugin'
gem 'oauth'
gem 'roxml'
gem 'edmunds_vin'
gem 'nhtsa_vin'
gem 'will_paginate'
gem 'rails-jquery-autocomplete'
gem 'jquery-rails', '~> 3.1.4'

View File

@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2017 Rick Barrette
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

View File

@@ -33,7 +33,7 @@ class CustomersController < ApplicationController
default_search_scope :names
autocomplete :customer, :name, :full => false, :extra_data => [:id]
autocomplete :customer, :name, :full => true, :extra_data => [:id]
def filter_vehicles_by_customer
@filtered_vehicles = Vehicle.all.where(customer_id: params[:selected_customer])

View File

@@ -18,7 +18,7 @@ class EstimateController < ApplicationController
# Downloads and forwards the estimate pdf
#
def show
base = QboEstimate.get_base.service
base = QboEstimate.get_base
estimate = base.fetch_by_id(params[:id])
@pdf = base.pdf(estimate)
send_data @pdf, filename: "estimate #{estimate.doc_number}.pdf", :disposition => 'inline', :type => "application/pdf"

View File

@@ -0,0 +1,96 @@
#The License
#
#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.
#
#Proprietary and confidential
#
#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.
# This controller class will handle map management
class LineItemsController < ApplicationController
unloadable
include AuthHelper
before_filter :require_user
# display all line items for an issue
def index
if params[:issue_id]
begin
@line_items = Issue.find_by_id(params[:issue_id]).line_items
rescue ActiveRecord::RecordNotFound
render_404
end
end
end
# return an HTML form for creating a new line item
def new
@line_item = LineItem.new
end
# create a new line item
def create
@line_item = LineItem.new(params[:line_item])
if @line_item.save
flash[:notice] = "New Line Item Created"
redirect_to @line_item.issue
else
flash[:error] = @line_item.errors.full_messages.to_sentence
redirect_to new_line_item_path
end
end
# display a specific line item
def show
begin
@line_item = LineItem.find_by_id(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
end
end
# return an HTML form for editing a line item
def edit
begin
@line_item = LineItem.find_by_id(params[:id])
rescue ActiveRecord::RecordNotFound
render_404
end
end
# update a specific line item
def update
begin
@line_item = LineItem.find_by_id(params[:id])
if @line_item.update_attributes(params[:line_item])
flash[:notice] = "Line Item updated"
redirect_to @line_item
else
flash[:error] = @line_item.errors.full_messages.to_sentence if @line_item.errors
redirect_to edit_line_item_path
end
rescue ActiveRecord::RecordNotFound
render_404
end
end
# delete a specific line item
def destroy
begin
line_item = LineItem.find_by_id(params[:id])
issue = line_item.issue
line_item.destroy
flash[:notice] = "Line Item deleted successfully"
redirect_to issue
rescue ActiveRecord::RecordNotFound
render_404
end
end
end

View File

@@ -144,6 +144,6 @@ class QboController < ApplicationController
ActiveRecord::Base.connection.close
end
redirect_to qbo_path(:redmine_qbo), :flash => { :notice => "Successfully synced to Quickbooks" }
redirect_to :back, :flash => { :notice => "Successfully synced to Quickbooks" }
end
end

View File

@@ -37,8 +37,7 @@ class VehiclesController < ApplicationController
# return an HTML form for creating a new vehicle
def new
@vehicle = Vehicle.new
@customers = Customer.all.order(:name)
@customer = params[:customer_id] if params[:customer_id]
@customer = Customer.find_by_id(params[:customer_id]) if params[:customer_id]
end
# create a new vehicle
@@ -67,8 +66,7 @@ class VehiclesController < ApplicationController
def edit
begin
@vehicle = Vehicle.find_by_id(params[:id])
@customer = @vehicle.customer.id
@customers = Customer.all.order(:name)
@customer = @vehicle.customer
rescue ActiveRecord::RecordNotFound
render_404
end

34
app/models/line_item.rb Normal file
View File

@@ -0,0 +1,34 @@
#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.
#
#Proprietary and confidential
#
#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 LineItem < ActiveRecord::Base
unloadable
belongs_to :issue
attr_accessible :amount, :description, :unit_price, :quantity, :item_id
validates_presence_of :amount, :description, :unit_price, :quantity
def add_to_invoice(invoice)
line_item = Quickbooks::Model::InvoiceLineItem.new
line_item.amount = amount
line_item.description = description
line_item.sales_item! do |detail|
detail.unit_price = unit_price
detail.quantity = quantity
detail.item_id = item_id # Item ID here... Where do i get this???
end
invoice.line_items << line_item
return invoice
end
end

View File

@@ -12,8 +12,6 @@ class Vehicle < ActiveRecord::Base
unloadable
API_KEY = Setting.plugin_redmine_qbo['settingsEdmundsAPIKey']
belongs_to :customer
has_many :issues, :foreign_key => 'vehicles_id'
@@ -21,8 +19,6 @@ class Vehicle < ActiveRecord::Base
validates_presence_of :customer
validates :vin, uniqueness: true
#validates :year, numericality: { only_integer: true }
before_save :decode_vin
after_initialize :get_details
@@ -30,7 +26,12 @@ class Vehicle < ActiveRecord::Base
# returns a human readable string
def to_s
return "#{year} #{make} #{model}"
if year.nil? or make.nil? or model.nil?
return "#{vin}"
else
split_vin = vin.scan(/.{1,9}/)
return "#{year} #{make} #{model} - #{split_vin[1]}"
end
end
# returns the raw JSON details from EMUNDS
@@ -41,7 +42,7 @@ class Vehicle < ActiveRecord::Base
# returns the style of the vehicle
def style
begin
return @details['years'][0]['styles'][0]['name'] if @details
return @details.trim if @details
rescue
return nil
end
@@ -49,12 +50,14 @@ class Vehicle < ActiveRecord::Base
# returns the drive of the vehicle i.e. 2 wheel, 4 wheel, ect.
def drive
return @details['drivenWheels'].to_s.upcase if @details
#todo fix this
#return @details.drive_type if @details
return nil
end
# returns the number of doors of the vehicle
def doors
return @details['numOfDoors'] if @details
return @details.doors if @details
end
# Force Upper Case for VIN numbers
@@ -71,8 +74,9 @@ class Vehicle < ActiveRecord::Base
# Force Upper Case for VIN numbers
def vin=(val)
# The to_s is in case you get nil/non-string
write_attribute(:vin, val.to_s.scan(/^[A-Za-z0-9]+$/).join.upcase)
#strip VIN of all illegal chars (for barcode scanner)
val = val.to_s.upcase.gsub(/[^A-HJ-NPR-Za-hj-npr-z\d]+/,"")
write_attribute(:vin, val)
end
# search for a vin
@@ -80,35 +84,29 @@ class Vehicle < ActiveRecord::Base
where("vin LIKE ?", "%#{search}%")
end
private
private
# init method to pull JSON details from Edmunds
def get_details
if self.vin?
begin
@details = JSON.parse get_decoder.full(self.vin)
raise @details['message'] if @details['status'].to_s.eql? "NOT_FOUND"
raise @details['message'] if @details['status'].to_s.eql? "BAD_REQUEST"
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
# returns the Edmunds decoder service
def get_decoder
#TODO API Code via Settings
return decoder = Edmunds::Vin.new(API_KEY)
end
# decodes a vin and updates self
def decode_vin
get_details
if @details
begin
self.year = @details['years'][0]['year']
self.make = @details['make']['name']
self.model = @details['model']['name']
self.year = @details.year unless @details.year.nil?
self.make = @details.make unless @details.make.nil?
self.model = @details.model unless @details.model.nil?
rescue Exception => e
errors.add(:vin, e.message)
end
@@ -116,15 +114,4 @@ class Vehicle < ActiveRecord::Base
self.name = to_s
end
# makes a squishvin
# https://api.edmunds.com/api/vehicle/v2/squishvins/#{vin}/?fmt=json&api_key=#{ENV['edmunds_key']}
def vin_squish
if not self.vin? or self.vin.size < 11
# this is to go ahead and query the API, letting them handle the error. :P
return '1000000000A'
end
v = self.vin[0,11]
return v.slice(0,8) + v.slice(9,11)
end
end

View File

@@ -0,0 +1,6 @@
<%= form_tag(customers_path, :method => "get", id: "search-form") do %>
<%= text_field_tag :search, params[:search], placeholder: "Search Customers" %>
<%= submit_tag "Search" %>
<% end %>
<%= button_to "New Customer", new_customer_path, method: :get%>
<%= button_to "Sync", qbo_sync_path, method: :get%>

View File

@@ -0,0 +1,2 @@
<h3>Customers</h3>
<%= render :partial => 'customers/search' %>

View File

@@ -1,10 +1,4 @@
<h1>Customers</h1>
<br/>
<%= form_tag(customers_path, :method => "get", id: "search-form") do %>
<%= text_field_tag :search, params[:search], placeholder: "Search Customers" %>
<%= submit_tag "Search" %>
<% end %>
<br/>
<h2>Customers <span style="float:right"> <%= render :partial => 'customers/search' %> </span> </h2>
<% if @customers.present? %>
<br/>
<% @customers.each do |c| %>
@@ -15,6 +9,8 @@
</div>
<% end %>
<p>Matching <%= @customers.count %> Customers </p>
<div class="actions">
<%= will_paginate @customers %>
</div>
@@ -24,5 +20,5 @@
<% end %>
<div>
<%= Customer.count %> Customers - <b>Last Sync: </b> <%= Qbo.last_sync if Qbo.exists? %>
<%= render :partial => 'qbo/stats' %>
</div>

View File

@@ -1,3 +1,3 @@
<h1>New Customer</h1>
<h2>New Customer</h2>
<br/>
<%= render :partial => 'customers/form' %>

View File

@@ -1,16 +1,15 @@
<div id="content">
<h2>Customer #<%= @customer.id %></h2>
<h2>Customer #<%= @customer.id %> - <%= @customer.name %> <span style="float:right"> <%= render :partial => 'customers/search' %> </span> </h2>
<br/>
<div class="subject">
<div><h3><%= @customer.name %></h3></div>
<div><h3>Details:</h3></div>
</div>
<div class="attributes">
<div class="splitcontent">
<div class="splitcontentleft">
<h4>Details:</h4>
<%= render :partial => 'customers/details', locals: {customer: @customer} %>
</div>
<div class="splitcontentleft">

View File

@@ -48,7 +48,7 @@ This customer link expires in <%= distance_of_time_in_words(Time.now, @token.exp
end
#end
end %>
<%= render_custom_fields_rows(@issue) %>
<%= render_full_width_custom_fields_rows(@issue) %>
<%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %>
</div>

View File

@@ -0,0 +1,29 @@
<% if issues && issues.any? %>
<%= form_tag({}) do %>
<table class="list issues">
<thead><tr>
<th>#</th>
<th><%=l(:field_project)%></th>
<th><%=l(:field_tracker)%></th>
<th><%=l(:field_subject)%></th>
</tr></thead>
<tbody>
<% for issue in issues %>
<tr id="issue-<%= h(issue.id) %>" class="hascontextmenu <%= cycle('odd', 'even') %> <%= issue.css_classes %>">
<td class="id">
<%= check_box_tag("ids[]", issue.id, false, :style => 'display:none;', :id => nil) %>
<%= link_to(issue.id, issue_path(issue)) %>
</td>
<td class="project"><%= link_to_project(issue.project) %></td>
<td class="tracker"><%= issue.tracker %></td>
<td class="subject">
<%= link_to(issue.subject.truncate(60), issue_path(issue)) %> (<%= issue.status %>)
</td>
</tr>
<% end %>
</tbody>
</table>
<% end %>
<% else %>
<p class="nodata"><%= l(:label_no_data) %></p>
<% end %>

View File

@@ -0,0 +1 @@
<b>Last Sync: </b> <%= Qbo.last_sync if Qbo.exists? %>

View File

@@ -20,14 +20,6 @@ intuit.ipp.anywhere.setup({menuProxy: '/path/to/blue-dot', grantUrl: '<%= qbo_au
<table >
<tbody>
<tr>
<th>Edmunds API Key</th>
<td>
<input type="text" style="width:350px" id="settingsEdmundsAPIKey"
value="<%= settings['settingsEdmundsAPIKey'] %>"
name="settings[settingsEdmundsAPIKey]" >
</td>
</tr>
<tr>
<th>Intuit QBO OAuth Consumer Key</th>

View File

@@ -0,0 +1 @@
<%= Customer.count %> Customers - <%= render :partial => 'qbo/last_sync' %>

View File

@@ -21,16 +21,6 @@
<td><%= vehicle.style %></td>
</tr>
<tr>
<th>Drive</th>
<td><%= vehicle.drive %></td>
</tr>
<tr>
<th>Doors</th>
<td><%= vehicle.doors %></td>
</tr>
<tr>
<th>Notes</th>
<td><%= vehicle.notes %></td>
@@ -45,7 +35,6 @@
<td/>
<td>
<%= button_to "New Issue", new_issue_path(:vehicle_id => vehicle.id, :customer_id => vehicle.customer.id), method: :get%>
<%= button_to "Edit", edit_vehicle_path(vehicle), method: :get%>
<%= button_to "Delete", vehicle, method: :delete, data: {confirm: "You sure?"} %>
</td>

View File

@@ -4,9 +4,10 @@
<%= form_for @vehicle do |f| %>
<div class="clearfix">
Customer:
<div class="input">
<%= f.collection_select :customer_id, @customers, :id, :name, include_blank: true, :selected => @customer, :required => true%>
Customer:
<div class="input">
<%= f.autocomplete_field :customer, autocomplete_customer_name_customers_path, :value => @customer.name, :update_elements => {:id => '#customer_id', :value => '#issue_customer'}, :required => true %>
<%= f.hidden_field :customer_id, :id => "customer_id", :value => @customer.id %>
</div>
</div>

View File

@@ -21,6 +21,8 @@
<%= will_paginate @vehicles %>
</div>
<p>Matching <%= @vehicles.count %> Vehicles </p>
<% else %>
<p>There are no vehicles containing the term(s) <%= params[:search] %>.</p>
<% end %>

View File

@@ -0,0 +1,4 @@
<%= form_tag(vehicles_path, :method => "get", id: "search-form") do %>
<%= text_field_tag :search, params[:search], placeholder: "Search Vehicles by VIN" %>
<%= submit_tag "Search" %>
<% end %>

View File

@@ -1,9 +1,4 @@
<h1>Customer Vehicles</h1>
<h2>Customer Vehicles <span style="float:right"> <%= render :partial => 'vehicles/search' %> </span> </h2>
<br/>
<%= form_tag(vehicles_path, :method => "get", id: "search-form") do %>
<%= text_field_tag :search, params[:search], placeholder: "Search Vehicles by VIN" %>
<%= submit_tag "Search" %>
<% end %>
<%= render :partial => 'vehicles/list' %>

View File

@@ -1,3 +1,3 @@
<h1>New Customer Vehicle</h1>
<h2>New Customer Vehicle</h2>
<br/>
<%= render :partial => 'vehicles/form' %>

View File

@@ -1,4 +1,4 @@
<h1>Vehicle #<%=@vehicle.id%> </h1>
<h2>Vehicle #<%=@vehicle.id%> <span style="float:right"> <%= render :partial => 'customers/search' %> </span> </h2>
<br/>
<div style="text-align: left; width:90%;">

View File

@@ -12,4 +12,12 @@ $(function() {
data: { selected_customer: $("input#issue_customer_id").val() }
});
});
$("input#project_customer_id").on("change", function() {
$.ajax({
url: "/filter_vehicles_by_customer",
type: "GET",
data: { selected_customer: $("input#project_customer_id").val() }
});
});
});

View File

@@ -1,3 +0,0 @@
Edmunds::Api.configure do |config|
config.api_key = '2dheutzvhxs28dzukx5tgu47'
end

View File

@@ -1,6 +1,6 @@
#The MIT License (MIT)
#
#Copyright (c) 2016 rick barrette
#Copyright (c) 2017 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,6 +17,7 @@ en:
field_qbo_invoice: "Invoice"
field_qbo_estimate: "Estimate"
field_vehicles: "Vehicle"
field_vehicle: "Vehicle"
field_vin: "VIN"
field_notes: "Notes"
field_qbo_billed: "Billed"

View File

@@ -1,6 +1,6 @@
#The MIT License (MIT)
#
#Copyright (c) 2016 rick barrette
#Copyright (c) 2017 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

@@ -0,0 +1,16 @@
#The MIT License (MIT)
#
#Copyright (c) 2017 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 UpdateProjects < ActiveRecord::Migration
def change
add_reference :projects, :customer, index: true
add_reference :projects, :vehicle, index: true
end
end

View File

@@ -0,0 +1,26 @@
#The License
#
#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.
#
#Proprietary and confidential
#
#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 CreateLineItems < ActiveRecord::Migration
def change
create_table :line_items do |t|
t.integer :item_id
t.float :amount
t.string :description
t.float :unit_price
t.float :quantity
t.boolean :billed
end
add_reference :line_items, :issues, index: true
end
end

21
init.rb
View File

@@ -16,9 +16,12 @@ Redmine::Plugin.register :redmine_qbo do
require_dependency 'issues_show_hook_listener'
require_dependency 'users_show_hook_listener'
require_dependency 'header_footer_hook_listener'
require_dependency 'projects_form_hook_listener'
require_dependency 'view_hook_listener'
# Patches to the Redmine core. Will not work in development mode
require_dependency 'issue_patch'
require_dependency 'project_patch'
require_dependency 'user_patch'
require_dependency 'query_patch'
require_dependency 'time_entry_query_patch'
@@ -28,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.5.0'
version '0.7.0'
url 'https://github.com/rickbarrette/redmine_qbo'
author_url 'http://rickbarrette.org'
settings :default => {'empty' => true}, :partial => 'qbo/settings'
@@ -41,12 +44,14 @@ Redmine::Plugin.register :redmine_qbo do
Issue.safe_attributes 'vehicles_id'
User.safe_attributes 'qbo_employee_id'
TimeEntry.safe_attributes 'qbo_billed'
Project.safe_attributes 'customer_id'
Project.safe_attributes 'vehicle_id'
# We are playing in the sandbox
#Quickbooks.sandbox_mode = true
# set per_page globally
WillPaginate.per_page = 10
WillPaginate.per_page = 20
permission :view_customers, :customers => :index, :public => false
permission :add_customers, :customers => :new, :public => false
@@ -55,13 +60,7 @@ Redmine::Plugin.register :redmine_qbo do
permission :view_vehicles, :payments => :new, :public => false
# Register QBO top menu item
#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.allowed_to?(:view_vehicles, @project) }
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.allowed_to?(:add_customers, @project) }
menu :application_menu, :new_payment, { :controller => :payments, :action => :new }, :caption => 'New Payment', :if => Proc.new { User.current.allowed_to?(:add_payments, @project)}
menu :project_menu, :customers, { :controller => 'customers', :action => 'new' }, :caption => 'New Customer', :after => :new_issue, :param => :project_id
menu :project_menu, :payments, { :controller => 'payments', :action => 'new' }, :caption => 'New Payment', :after => :customers, :param => :project_id
end

View File

@@ -41,7 +41,9 @@ module IssuePatch
# Create billable time entries
def bill_time
return if assigned_to.nil?
# Get unbilled time entries
spent_time = time_entries.where(qbo_billed: [false, nil])
spent_hours ||= spent_time.sum(:hours) || 0

View File

@@ -21,6 +21,10 @@ class IssuesFormHookListener < Redmine::Hook::ViewListener
# Show a dropdown for quickbooks contacts
def view_issues_form_details_bottom(context={})
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
# Check to see if there is a quickbooks user attached to the issue
selected_customer = context[:issue].customer ? context[:issue].customer.id : nil
@@ -33,7 +37,11 @@ class IssuesFormHookListener < Redmine::Hook::ViewListener
customer_id = f.hidden_field :customer_id, :id => "issue_customer_id"
if context[:issue].customer
vehicles = customer.vehicles.pluck(:name, :id).sort!
if customer.vehicles
vehicles = customer.vehicles.pluck(:name, :id)
else
vehicles = [nil].compact
end
estimates = customer.qbo_estimates.pluck(:doc_number, :id).sort! {|x, y| y <=> x}
else
vehicles = [nil].compact

40
lib/project_patch.rb Normal file
View File

@@ -0,0 +1,40 @@
#The MIT License (MIT)
#
#Copyright (c) 2017 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.
require_dependency 'project'
# Patches Redmine's Projects dynamically.
# Adds a relationships
module ProjectPatch
def self.included(base) # :nodoc:
base.extend(ClassMethods)
base.send(:include, InstanceMethods)
# Same as typing in the class
base.class_eval do
unloadable # Send unloadable so it will not be unloaded in development
belongs_to :customer, primary_key: :id
belongs_to :vehicle, primary_key: :id
end
end
end
module ClassMethods
end
module InstanceMethods
end
# Add module to Project
Project.send(:include, ProjectPatch)

View File

@@ -0,0 +1,36 @@
#The MIT License (MIT)
#
#Copyright (c) 2017 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 ProjectsFormHookListener < Redmine::Hook::ViewListener
# Edit Project Form
def view_projects_form(context={})
f = context[:form]
# Check to see if there is a quickbooks user attached to the issue
selected_customer = context[:project].customer ? context[:project].customer : nil
selected_vehicle = context[:project].vehicle_id ? context[:project].vehicle_id : nil
# Load customer information
customer = Customer.find_by_id(selected_customer) if selected_customer
search_customer = f.autocomplete_field :customer, autocomplete_customer_name_customers_path, :selected => selected_customer, :update_elements => {:id => '#project_customer_id', :value => '#project_customer'}
customer_id = f.hidden_field :customer_id, :id => "project_customer_id"
if context[:project].customer
vehicles = customer.vehicles.pluck(:name, :id).sort!
else
vehicles = [nil].compact
end
vehicle = f.select :vehicle_id, vehicles, :selected => selected_vehicle, include_blank: true
return "<p><label for=\"project_customer\">Customer</label>#{search_customer} #{customer_id}</p> <p>#{vehicle}</p>"
end
end

View File

@@ -52,16 +52,16 @@ module QueryPatch
# :order => @available_filters.size + 1},
#}
qbo_filters = {
"customer_id" => {
:id => :customer_id,
:type => :list_optional,
:order => @available_filters.size + 1,
#qbo_filters = {
# "customer_id" => {
# :id => :customer_id,
# :type => :list_optional
#:order => @available_filters.size + 1,
#:values => Customer.find(:all).collect { |c| [c.name, c.id.to_s]}
}
}
# }
#}
@available_filters.merge!(qbo_filters)
#@available_filters.merge!(qbo_filters)
end
@available_filters
end

View File

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