mirror of
https://github.com/rickbarrette/redmine_qbo.git
synced 2025-11-09 09:24:23 -05:00
Compare commits
285 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8812d7a11d | |||
| fe1388108c | |||
| b98cdbec8b | |||
| d7f8e51c4a | |||
| 2ce4e8b346 | |||
| dd8bf530c9 | |||
| 1103355cb7 | |||
| e400a7cee3 | |||
| b32df9525c | |||
| 967a727392 | |||
| 79539dd57f | |||
| 9d96c813e9 | |||
| 073447a4ce | |||
| 1a6b6fb321 | |||
| 543841ad64 | |||
| 5ad4b53b30 | |||
| bfec64de54 | |||
| 198e2d5934 | |||
| 95c39fb9c5 | |||
| dc7ee21aad | |||
| 8c8966a859 | |||
| b95aa41b3d | |||
| 2012774ae8 | |||
| be7e9ef2cc | |||
| 5ac1c44e41 | |||
| b1120c3849 | |||
| 847669376f | |||
| c2b62fbf88 | |||
| 21d0d25c78 | |||
| 365a36390b | |||
| 68846f7910 | |||
| 3a64e0d5bd | |||
| 2f5607b8ef | |||
| 2a713d7f13 | |||
| 61f0ee6f81 | |||
| d6a75f8ed0 | |||
| 14d10bd0e6 | |||
| bb9f251e69 | |||
| f2a7c034d0 | |||
| 3589ea8685 | |||
| f71be3f1a5 | |||
| cf3983adc4 | |||
| 55f6666328 | |||
| 933a0bb12d | |||
| 0c5d2195d2 | |||
| 3518f0acfe | |||
| d4bcccf598 | |||
| 3aa2798463 | |||
| 0cb9863000 | |||
| deb648e3d6 | |||
| 384854d701 | |||
| f192fe8d5f | |||
| 8a89af88c3 | |||
| a4944f2a3b | |||
| 0160a58c86 | |||
| f7d809e90a | |||
| 8e80277a15 | |||
| 962e954d6e | |||
| 9d1bfa3951 | |||
| b20a343cd2 | |||
| 593d8e603b | |||
| 5a5a531700 | |||
| f7464e1d47 | |||
| c39c9ab95c | |||
| 5aa32b735f | |||
| 9d4774a31b | |||
| 63c446673f | |||
| 8d1038ac0c | |||
| ae903bede2 | |||
| b82d5091b7 | |||
| 48a2c0cb45 | |||
| 092fa85dd8 | |||
| 5100b111e9 | |||
| 69273b5dc8 | |||
| 52bf4f916d | |||
| 7fa41d6574 | |||
| bbe1b22020 | |||
| 892e57084d | |||
| d6e15f90ef | |||
| 39872107bc | |||
| 1de5712f8c | |||
| 38518fa5e9 | |||
| 46df203e00 | |||
| 642406365d | |||
| ee40dbf5a4 | |||
| 828a59f989 | |||
| 79d05350db | |||
| 12aeb22ac3 | |||
| 09f531ce39 | |||
| 2b4bba7a88 | |||
| 9e2f9f5a7a | |||
| 7a6e35adff | |||
| 5047077b07 | |||
| 5242ed5a94 | |||
| a316c0f8d6 | |||
| 3c6bce713b | |||
| d4b0ac1e38 | |||
| d80df26df1 | |||
| dd9c945fcc | |||
| 43e6fff6d8 | |||
| 1650577d4d | |||
| c2b798e32c | |||
| 1817095d00 | |||
| 27bb6ea4a2 | |||
| ef2aebb7e4 | |||
| bc23dfad56 | |||
| 3be2a73422 | |||
| 4896241df1 | |||
| 75b159bf4e | |||
| 75a651dd3b | |||
| 76e230770b | |||
| 70d7b65741 | |||
| c251dc8407 | |||
| 6464904caf | |||
| 98cf1ed140 | |||
| 0d367245ce | |||
| c053c91a64 | |||
| 8f5467638e | |||
| 723d9024c1 | |||
| 8374f0ae3e | |||
| 4f9a3ed046 | |||
| cd5ed4f3a7 | |||
| 792773b945 | |||
| d09f5e9afe | |||
| f07c90cf90 | |||
| 774ce239c2 | |||
| 3ec2670209 | |||
| 76cbb42309 | |||
| 3e9138f802 | |||
| 1ac799d318 | |||
| 5e4d7c7686 | |||
| 6a6fcf093c | |||
| ab68b1cf89 | |||
| b692abcb64 | |||
| 94fe2f2d05 | |||
| 26f861a493 | |||
| 0f94675ba5 | |||
| 4816229c5e | |||
| 98b2d2078c | |||
| 6cf2a2640b | |||
| 58c9544a22 | |||
| d14b6a1b54 | |||
| ec37439577 | |||
| b5716595e4 | |||
| d968885e1e | |||
| 1231b776b7 | |||
| f8f3870405 | |||
| 723ad5bc53 | |||
| c4c4d6156d | |||
| 28eaecabff | |||
| 6819e2a826 | |||
| 9e1ae8d40b | |||
| 467677d73c | |||
| ee2be0d8c9 | |||
| 0fb358edf3 | |||
| 9972e18632 | |||
| b1c0f965f8 | |||
| d523584ca1 | |||
| de7b534572 | |||
| 24e3c29353 | |||
| 800058a3c0 | |||
| 9c230d6423 | |||
| 497f4661bd | |||
| d5b1187076 | |||
| 0468af9e53 | |||
| 8ac9cfae93 | |||
| c07e4cb427 | |||
| 380315e207 | |||
| 950dfd9af9 | |||
| c4abee7415 | |||
| 27d282f244 | |||
| 610582bce8 | |||
| 655f0c3dcd | |||
| d9ce3981a3 | |||
| 8e6bdc6d38 | |||
| 0a167c33ee | |||
| b8002df5af | |||
| 7356e99981 | |||
| 4eb2718b2b | |||
| 3c849ca5dd | |||
| dd40c045cf | |||
| 6505f8114d | |||
| eeb5e96aae | |||
| 9c61ecd0c0 | |||
| a771085a33 | |||
| 5f1dea77cc | |||
| 9ae11bac49 | |||
| 77239122c0 | |||
| 804b4066ce | |||
| 2cc8c83071 | |||
| a7ba05db29 | |||
| a8c353945d | |||
| f90921fb00 | |||
| 84fd9770c9 | |||
| f7ee1dbcec | |||
| d8d0572a1a | |||
| 1688ebd327 | |||
| 26d4f09f97 | |||
| fe831cf259 | |||
| 35e4c5f9da | |||
| 66779e60f6 | |||
| 1e0e608ca1 | |||
| fbf074e3c3 | |||
| a33a1eeb94 | |||
| 266a3ed921 | |||
| fc0a416175 | |||
| e561b52e9b | |||
| 51586800a0 | |||
| fec1512930 | |||
| 559a846f1e | |||
| f936e61684 | |||
| dfd7dfcf79 | |||
| a409514467 | |||
| 8b44e5b51b | |||
| e350657d89 | |||
| 7fea7042a5 | |||
| 9c57878fd1 | |||
| fd6c70860c | |||
| 48114de428 | |||
| 7267bac43d | |||
| fd92d72ac3 | |||
| 5457ae51c2 | |||
| 6647e533df | |||
| 6d961addbe | |||
| af654ef9d0 | |||
| 3b5f6691fb | |||
| aa770bb1f0 | |||
| 0c2d57c826 | |||
| fa3322d530 | |||
| 3bbc0a16ff | |||
| 2e7c26d90e | |||
| 75fae59bcf | |||
| a1b72a1dd0 | |||
| 4c9b48bd91 | |||
| e3c7047999 | |||
| 07fe12e65b | |||
| 8955053e64 | |||
| b534e94c38 | |||
| 64a7b4fb29 | |||
| a6c88c85c4 | |||
| bbed60c2ca | |||
| 20b31d47be | |||
| b48a21c8fe | |||
| d4930227ba | |||
| ad27e98e48 | |||
| 29fa5ff510 | |||
| b08f3adc39 | |||
| 40f2358c26 | |||
| 952baff6f2 | |||
| 31797585c8 | |||
| 2d50c4c454 | |||
| ae491e0902 | |||
| c205479975 | |||
| ffede26929 | |||
| 8a553c0129 | |||
| d5f9cee2e1 | |||
| 28d5187d40 | |||
| 48709cdceb | |||
| 22a6492e8b | |||
| 82f4ced404 | |||
| 3348173cbe | |||
| 9d83175bd7 | |||
| b6f8304d13 | |||
| cd31ecf1f0 | |||
| 15cd8e756c | |||
| 3caaaa52dd | |||
| 03b12d08ef | |||
| a69ad1cef2 | |||
| eb3d8160a1 | |||
| 9cd5440bc8 | |||
| f10114d43c | |||
| fd8a068f16 | |||
| e115e8fe94 | |||
| b4255135c4 | |||
| 9fc94e93c6 | |||
| 3e50c037e0 | |||
| dc5b8419a4 | |||
| c1002cb93c | |||
| fcaa5f55cf | |||
| 195359ce0b | |||
| b5c8823dda | |||
| d9317e717b | |||
| 9e9c111256 | |||
| c254c78ee5 | |||
| dc1edaa651 |
4
Gemfile
4
Gemfile
@@ -1,8 +1,10 @@
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'quickbooks-ruby', :git => 'https://github.com/rickbarrette/quickbooks-ruby.git'
|
||||
gem 'quickbooks-ruby'
|
||||
gem 'quickbooks-ruby-base'
|
||||
gem 'oauth-plugin'#, '~> 0.5.1'
|
||||
gem 'oauth'
|
||||
gem 'roxml'
|
||||
gem 'nokogiri'
|
||||
gem 'edmunds_vin'
|
||||
gem 'will_paginate', '~> 3.1.0'
|
||||
|
||||
63
app/controllers/customers_controller.rb
Normal file
63
app/controllers/customers_controller.rb
Normal file
@@ -0,0 +1,63 @@
|
||||
#The MIT License (MIT)
|
||||
#
|
||||
#Copyright (c) 2016 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.
|
||||
|
||||
# This controller class will handle map management
|
||||
class CustomersController < ApplicationController
|
||||
unloadable
|
||||
|
||||
include AuthHelper
|
||||
|
||||
before_filter :require_user
|
||||
|
||||
# display a list of all customers
|
||||
def index
|
||||
@customers = QboCustomer.paginate(:page => params[:page])
|
||||
end
|
||||
|
||||
def new
|
||||
|
||||
end
|
||||
|
||||
def create
|
||||
|
||||
end
|
||||
|
||||
# display a specific customer
|
||||
def show
|
||||
@customer = QboCustomer.find_by_id(params[:id])
|
||||
if @customer
|
||||
@vehicles = @customer.vehicles.paginate(:page => params[:page])
|
||||
else
|
||||
flash[:error] = "Customer Not Found"
|
||||
render :index
|
||||
end
|
||||
end
|
||||
|
||||
# return an HTML form for editing a customer
|
||||
def edit
|
||||
@customer = QboCustomer.find_by_id(params[:id])
|
||||
end
|
||||
|
||||
# update a specific customer
|
||||
def update
|
||||
@customer = QboCustomer.find_by_id(params[:id])
|
||||
if @customer.update_attributes(params[:customer])
|
||||
flash[:success] = "Customer updated"
|
||||
redirect_to @customer
|
||||
else
|
||||
render :edit
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -10,13 +10,18 @@
|
||||
class EstimateController < ApplicationController
|
||||
unloadable
|
||||
|
||||
include AuthHelper
|
||||
|
||||
before_filter :require_user
|
||||
|
||||
#
|
||||
# Downloads and forwards the estimate pdf
|
||||
#
|
||||
def show
|
||||
base = QboEstimate.get_base.service
|
||||
@pdf = base.pdf(base.fetch_by_id(params[:id]))
|
||||
send_data @pdf, filename: "estimate.pdf", :disposition => 'inline', :type => "application/pdf"
|
||||
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"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -10,12 +10,17 @@
|
||||
class InvoiceController < ApplicationController
|
||||
unloadable
|
||||
|
||||
include AuthHelper
|
||||
|
||||
before_filter :require_user
|
||||
|
||||
#
|
||||
# Downloads and forwards the invoice pdf
|
||||
#
|
||||
def show
|
||||
base = QboInvoice.get_base.service
|
||||
@pdf = base.pdf(base.fetch_by_id(params[:id]))
|
||||
send_data @pdf, filename: "invoice.pdf", :disposition => 'inline', :type => "application/pdf"
|
||||
invoice = base.fetch_by_id(params[:id])
|
||||
@pdf = base.pdf(invoice)
|
||||
send_data @pdf, filename: "invoice #{invoice.doc_number}.pdf", :disposition => 'inline', :type => "application/pdf"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,6 +11,10 @@
|
||||
class QboController < ApplicationController
|
||||
unloadable
|
||||
|
||||
include AuthHelper
|
||||
|
||||
before_filter :require_user
|
||||
|
||||
#
|
||||
# Called when the QBO Top Menu us shown
|
||||
#
|
||||
|
||||
76
app/controllers/vehicles_controller.rb
Normal file
76
app/controllers/vehicles_controller.rb
Normal file
@@ -0,0 +1,76 @@
|
||||
#The MIT License (MIT)
|
||||
#
|
||||
#Copyright (c) 2016 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.
|
||||
|
||||
# This controller class will handle map management
|
||||
class VehiclesController < ApplicationController
|
||||
unloadable
|
||||
|
||||
include AuthHelper
|
||||
|
||||
before_filter :require_user
|
||||
|
||||
# display a list of all vehicles
|
||||
def index
|
||||
@vehicles = Vehicle.paginate(:page => params[:page])
|
||||
end
|
||||
|
||||
# return an HTML form for creating a new vehicle
|
||||
def new
|
||||
@vehicle = Vehicle.new
|
||||
end
|
||||
|
||||
# create a new vehicle
|
||||
def create
|
||||
@vehicle = Vehicle.new(params[:vehicle])
|
||||
@vehicle.save!
|
||||
redirect_to @vehicle
|
||||
end
|
||||
|
||||
# display a specific vehicle
|
||||
def show
|
||||
@vehicle = Vehicle.find_by_id(params[:id])
|
||||
if @vehicle
|
||||
@customer = @vehicle.qbo_customer.name if @vehicle.qbo_customer
|
||||
else
|
||||
flash[:error] = "Vehicle Not Found"
|
||||
render :index
|
||||
end
|
||||
end
|
||||
|
||||
# return an HTML form for editing a vehicle
|
||||
def edit
|
||||
@vehicle = Vehicle.find_by_id(params[:id])
|
||||
@customer = @vehicle.qbo_customer.id if @vehicle.qbo_customer
|
||||
end
|
||||
|
||||
# update a specific vehicle
|
||||
def update
|
||||
@vehicle = Vehicle.find_by_id(params[:id])
|
||||
if @vehicle.update_attributes(params[:vehicle])
|
||||
flash[:success] = "Vehicle updated"
|
||||
redirect_to @vehicle
|
||||
else
|
||||
render :edit
|
||||
end
|
||||
end
|
||||
|
||||
# delete a specific vehicle
|
||||
def destroy
|
||||
v = Vehicle.find_by_id(params[:id])
|
||||
if v != nil
|
||||
v.destroy
|
||||
flash.now[:notice] = "Vehicle deleted successfully"
|
||||
else
|
||||
flash.now[:error] = "No Vehicle Found"
|
||||
end
|
||||
redirect_to action: :index
|
||||
end
|
||||
|
||||
end
|
||||
18
app/helpers/auth_helper.rb
Normal file
18
app/helpers/auth_helper.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
#The MIT License (MIT)
|
||||
#
|
||||
#Copyright (c) 2016 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.
|
||||
|
||||
module AuthHelper
|
||||
|
||||
def require_user
|
||||
if !User.current.logged?
|
||||
render :file => "public/401.html.erb", :status => :unauthorized, :layout =>true
|
||||
end
|
||||
end
|
||||
end
|
||||
13
app/helpers/vehicle_helper.rb
Normal file
13
app/helpers/vehicle_helper.rb
Normal file
@@ -0,0 +1,13 @@
|
||||
#The MIT License (MIT)
|
||||
#
|
||||
#Copyright (c) 2016 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.
|
||||
|
||||
module VehicleHelper
|
||||
|
||||
end
|
||||
@@ -10,33 +10,119 @@
|
||||
|
||||
class QboCustomer < ActiveRecord::Base
|
||||
unloadable
|
||||
|
||||
has_many :issues
|
||||
has_many :qbo_purchases
|
||||
has_many :vehicles
|
||||
|
||||
attr_accessible :name
|
||||
validates_presence_of :id, :name
|
||||
|
||||
def self.get_base
|
||||
after_initialize :get_details
|
||||
|
||||
self.primary_key = :id
|
||||
|
||||
# returns true if the customer is active
|
||||
def active?
|
||||
return @details.active? if @details
|
||||
end
|
||||
|
||||
# returns a human readable string
|
||||
def to_s
|
||||
return name
|
||||
end
|
||||
|
||||
# returns the customer's email
|
||||
def email
|
||||
begin
|
||||
return @details.email_address.address
|
||||
rescue
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
# returns the customer's primary phone
|
||||
def primary_phone
|
||||
begin
|
||||
return @details.primary_phone.free_form_number
|
||||
rescue
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
# returns the customer's mobile phone
|
||||
def mobile_phone
|
||||
begin
|
||||
return @details.mobile_phone.free_form_number
|
||||
rescue
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
# returns the customer's notes
|
||||
def notes
|
||||
return @details.notes if @details
|
||||
end
|
||||
|
||||
# updates the customer's notes in QBO
|
||||
def notes=(s)
|
||||
customer = get_customer(self.id)
|
||||
customer.notes = s
|
||||
get_base.update(customer)
|
||||
end
|
||||
|
||||
# returns the bases QBO service for customers
|
||||
def get_base
|
||||
Qbo.get_base(:customer)
|
||||
end
|
||||
|
||||
def self.get_customer (id)
|
||||
get_base.service.find_by_id(id)
|
||||
# returns the QBO customer
|
||||
def get_customer (id)
|
||||
get_base.find_by_id(id)
|
||||
end
|
||||
|
||||
def self.update_all
|
||||
# proforms a bruteforce sync operation
|
||||
# This needs to be simplified
|
||||
def update_all
|
||||
customers = get_base.service.all
|
||||
|
||||
transaction do
|
||||
# Update the customer table
|
||||
customers.each { |customer|
|
||||
qbo_customer = QboCustomer.find_or_create_by(id: customer.id)
|
||||
# only update if diffrent
|
||||
if not qbo_customer.name == customer.display_name
|
||||
qbo_customer.name = customer.display_name
|
||||
qbo_customer.id = customer.id
|
||||
qbo_customer.save!
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
#remove deleted customers
|
||||
# remove deleted customers
|
||||
where.not(customers.map(&:id)).destroy_all
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# init details
|
||||
def get_details
|
||||
if self.id
|
||||
@details = get_customer(self.id)
|
||||
update
|
||||
end
|
||||
end
|
||||
|
||||
# update's the customers name if updated
|
||||
def update
|
||||
begin
|
||||
if not self.name == @detils.display_name
|
||||
self.name = @details.display_name
|
||||
self.save
|
||||
end
|
||||
rescue
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
88
app/models/vehicle.rb
Normal file
88
app/models/vehicle.rb
Normal file
@@ -0,0 +1,88 @@
|
||||
#The MIT License (MIT)
|
||||
#
|
||||
#Copyright (c) 2016 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 Vehicle < ActiveRecord::Base
|
||||
|
||||
unloadable
|
||||
belongs_to :qbo_customer
|
||||
|
||||
attr_accessible :year, :make, :model, :qbo_customer_id, :notes, :vin
|
||||
validates_presence_of :year, :make, :model, :qbo_customer_id
|
||||
|
||||
before_validation :decode_vin
|
||||
after_initialize :get_details
|
||||
|
||||
# returns a human readable string
|
||||
def to_s
|
||||
return "#{self.year} #{self.make} #{self.model}"
|
||||
end
|
||||
|
||||
# returns the raw JSON details from EMUNDS
|
||||
def details
|
||||
return @details
|
||||
end
|
||||
|
||||
# returns the style of the vehicle
|
||||
def style
|
||||
begin
|
||||
return @details['years'][0]['styles'][0]['name'] if @details
|
||||
rescue
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
# returns the drive of the vehicle i.e. 2 wheel, 4 wheel, ect.
|
||||
def drive
|
||||
return @details['drivenWheels'] if @details
|
||||
end
|
||||
|
||||
# returns the number of doors of the vehicle
|
||||
def doors
|
||||
return @details['numOfDoors'] if @details
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# init method to pull JSON details from Edmunds
|
||||
def get_details
|
||||
if self.vin?
|
||||
@details = JSON.parse get_decoder.full(self.vin)
|
||||
end
|
||||
end
|
||||
|
||||
# returns the Edmunds decoder service
|
||||
def get_decoder
|
||||
#TODO API Code via Settings
|
||||
return decoder = Edmunds::Vin.new('2dheutzvhxs28dzukx5tgu47')
|
||||
end
|
||||
|
||||
# decodes a vin and updates self
|
||||
def decode_vin
|
||||
get_details
|
||||
if self.vin?
|
||||
details
|
||||
self.year = @details['years'][0]['year']
|
||||
self.make = @details['make']['name']
|
||||
self.model = @details['model']['name']
|
||||
end
|
||||
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
|
||||
35
app/views/customers/_details.html.erb
Normal file
35
app/views/customers/_details.html.erb
Normal file
@@ -0,0 +1,35 @@
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Customer</th>
|
||||
<td><%= @customer.name %></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>Email</th>
|
||||
<td><%= @customer.email %></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>Primary Phone</th>
|
||||
<td><%= @customer.primary_phone %></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>Mobile Phone</th>
|
||||
<td><%= @customer.mobile_phone %></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>Notes</th>
|
||||
<td><%= @customer.notes %></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td/>
|
||||
<td>
|
||||
<%= button_to "Edit", edit_customer_path(@customer), method: :get%>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
20
app/views/customers/index.html.erb
Normal file
20
app/views/customers/index.html.erb
Normal file
@@ -0,0 +1,20 @@
|
||||
<h1>Customers</h1>
|
||||
<br/>
|
||||
<% @customers.each do |c| %>
|
||||
<div class="row">
|
||||
<div class="span6 columns">
|
||||
<fieldset>
|
||||
<% @customer = c %>
|
||||
<%= render :partial => 'customers/details' %>
|
||||
<%= button_to "Show", customer_path(c.id), method: :get %>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<br/>
|
||||
|
||||
<div class="actions">
|
||||
<%= will_paginate @customers %>
|
||||
<%= button_to "New", new_customer_path, method: :get %>
|
||||
</div>
|
||||
5
app/views/customers/show.html.erb
Normal file
5
app/views/customers/show.html.erb
Normal file
@@ -0,0 +1,5 @@
|
||||
<h1>Customer Detail</h1>
|
||||
<br/>
|
||||
<%= render :partial => 'customers/details' %>
|
||||
<br/>
|
||||
<%= render :partial => 'vehicles/list' %>
|
||||
1
app/views/public/401.html.erb
Normal file
1
app/views/public/401.html.erb
Normal file
@@ -0,0 +1 @@
|
||||
<%= flash.now[:error] = "Not Authorized" %>
|
||||
46
app/views/vehicles/_details.html.erb
Normal file
46
app/views/vehicles/_details.html.erb
Normal file
@@ -0,0 +1,46 @@
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Customer</th>
|
||||
<td><%= @customer %></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>Vehicle</th>
|
||||
<td><%= @vehicle.to_s %></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>VIN</th>
|
||||
<td><%= @vehicle.vin %></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th>Style</th>
|
||||
<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>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td/>
|
||||
<td>
|
||||
<%= button_to "Edit", edit_vehicle_path(@vehicle), method: :get%>
|
||||
<%= button_to "Delete", @vehicle, method: :delete, data: {confirm: "You sure?"} %>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
2
app/views/vehicles/_dropdown.html.erb
Normal file
2
app/views/vehicles/_dropdown.html.erb
Normal file
@@ -0,0 +1,2 @@
|
||||
<%= @f.collection_select :vehicle_id, @customer.vehicles.order(:year), :id, :vin, include_blank: true, :selected => @vehicle%>
|
||||
Partial Test
|
||||
55
app/views/vehicles/_form.html.erb
Normal file
55
app/views/vehicles/_form.html.erb
Normal file
@@ -0,0 +1,55 @@
|
||||
<div class="row">
|
||||
<div class="span6 columns">
|
||||
<fieldset>
|
||||
|
||||
<%= form_for @vehicle do |f| %>
|
||||
<div class="clearfix">
|
||||
Customer:
|
||||
<div class="input">
|
||||
<%= f.collection_select :qbo_customer_id, QboCustomer.order(:name), :id, :name, include_blank: true, :selected => @customer, :required => true%>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="clearfix">
|
||||
Year:
|
||||
<div class="input">
|
||||
<%= f.text_field :year, :required => true %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="clearfix">
|
||||
Make:
|
||||
<div class="input">
|
||||
<%= f.text_field :make, :required => true %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="clearfix">
|
||||
Model:
|
||||
<div class="input">
|
||||
<%= f.text_field :model, :required => true %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="clearfix">
|
||||
VIN:
|
||||
<div class="input">
|
||||
<%= f.text_field :vin %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="clearfix">
|
||||
Notes:
|
||||
<div class="input">
|
||||
<%= f.text_area :notes, :rows => 6, :class => 'wiki-edit', :accesskey => accesskey(:edit), :cols => 60%>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<%= f.submit %>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
18
app/views/vehicles/_list.html.erb
Normal file
18
app/views/vehicles/_list.html.erb
Normal file
@@ -0,0 +1,18 @@
|
||||
<% @vehicles.each do |vehicle| %>
|
||||
<div class="row">
|
||||
<div class="span6 columns">
|
||||
<fieldset>
|
||||
<% @customer = vehicle.qbo_customer.name if vehicle.qbo_customer %>
|
||||
<% @vehicle = vehicle %>
|
||||
<%= render :partial => 'vehicles/details' %>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<br/>
|
||||
|
||||
<div class="actions">
|
||||
<%= will_paginate @vehicles %>
|
||||
<%= button_to "New", new_vehicle_path, method: :get %>
|
||||
</div>
|
||||
3
app/views/vehicles/edit.html.erb
Normal file
3
app/views/vehicles/edit.html.erb
Normal file
@@ -0,0 +1,3 @@
|
||||
<h1>Edit Customer Vehicle</h1>
|
||||
<br/>
|
||||
<%= render :partial => 'vehicles/form' %>
|
||||
3
app/views/vehicles/index.html.erb
Normal file
3
app/views/vehicles/index.html.erb
Normal file
@@ -0,0 +1,3 @@
|
||||
<h1>Customer Vehicles</h1>
|
||||
<br/>
|
||||
<%= render :partial => 'vehicles/list' %>
|
||||
3
app/views/vehicles/new.html.erb
Normal file
3
app/views/vehicles/new.html.erb
Normal file
@@ -0,0 +1,3 @@
|
||||
<h1>New Customer Vehicle</h1>
|
||||
<br/>
|
||||
<%= render :partial => 'vehicles/form' %>
|
||||
3
app/views/vehicles/show.html.erb
Normal file
3
app/views/vehicles/show.html.erb
Normal file
@@ -0,0 +1,3 @@
|
||||
<h1>Customer Vehicle</h1>
|
||||
<br/>
|
||||
<%= render :partial => 'vehicles/details' %>
|
||||
3
config/initializers/edmunds.rb
Normal file
3
config/initializers/edmunds.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
Edmunds::Api.configure do |config|
|
||||
config.api_key = '2dheutzvhxs28dzukx5tgu47'
|
||||
end
|
||||
@@ -16,3 +16,6 @@ en:
|
||||
field_qbo_employee: "Employee"
|
||||
field_qbo_invoice: "Invoice"
|
||||
field_qbo_estimate: "Estimate"
|
||||
field_vehicles: "Vehicle"
|
||||
field_vin: "VIN"
|
||||
field_notes: "Notes"
|
||||
|
||||
@@ -17,3 +17,5 @@ get 'qbo/oauth_callback', :to => 'qbo#oauth_callback'
|
||||
get 'qbo/sync', :to => 'qbo#sync'
|
||||
get 'qbo/estimate/:id', :to => 'estimate#show', as: :estimate
|
||||
get 'qbo/invoice/:id', :to => 'invoice#show', as: :invoice
|
||||
resources :vehicles
|
||||
resources :customers
|
||||
|
||||
24
db/migrate/016_create_vehicles.rb
Normal file
24
db/migrate/016_create_vehicles.rb
Normal file
@@ -0,0 +1,24 @@
|
||||
#The MIT License (MIT)
|
||||
#
|
||||
#Copyright (c) 2016 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 CreateVehicles < ActiveRecord::Migration
|
||||
|
||||
def change
|
||||
create_table :vehicles do |t|
|
||||
t.integer :year
|
||||
t.string :make
|
||||
t.string :model
|
||||
t.string :vin
|
||||
t.text :notes
|
||||
end
|
||||
|
||||
add_reference :vehicles, :qbo_customer, index: true
|
||||
end
|
||||
end
|
||||
15
db/migrate/017_update_issues_with_vehicles.rb
Normal file
15
db/migrate/017_update_issues_with_vehicles.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
#The MIT License (MIT)
|
||||
#
|
||||
#Copyright (c) 2016 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 UpdateIssuesWithVehicles < ActiveRecord::Migration
|
||||
def change
|
||||
add_reference :issues, :vehicles, index: true
|
||||
end
|
||||
end
|
||||
15
db/migrate/018_update_vehicles.rb
Normal file
15
db/migrate/018_update_vehicles.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
#The MIT License (MIT)
|
||||
#
|
||||
#Copyright (c) 2016 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 UpdateVehicles < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :vehicles, :name, :text
|
||||
end
|
||||
end
|
||||
13
init.rb
13
init.rb
@@ -19,11 +19,13 @@ Redmine::Plugin.register :redmine_qbo do
|
||||
# Patches to the Redmine core. Will not work in development mode
|
||||
require_dependency 'issue_patch'
|
||||
require_dependency 'user_patch'
|
||||
require_dependency 'query_patch'
|
||||
require_dependency 'pdf_patch'
|
||||
|
||||
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.4'
|
||||
version '0.0.5'
|
||||
url 'https://github.com/rickbarrette/redmine_qbo'
|
||||
author_url 'http://rickbarrette.org'
|
||||
settings :default => {'empty' => true}, :partial => 'qbo/settings'
|
||||
@@ -33,13 +35,18 @@ Redmine::Plugin.register :redmine_qbo do
|
||||
Issue.safe_attributes 'qbo_item_id'
|
||||
Issue.safe_attributes 'qbo_estimate_id'
|
||||
Issue.safe_attributes 'qbo_invoice_id'
|
||||
Issue.safe_attributes 'vehicles_id'
|
||||
User.safe_attributes 'qbo_employee_id'
|
||||
TimeEntry.safe_attributes 'qbo_billed'
|
||||
|
||||
# We are playing in the sandbox
|
||||
#Quickbooks.sandbox_mode = true
|
||||
|
||||
# Register QBO top menu item
|
||||
menu :top_menu, :qbo, { :controller => 'qbo', :action => 'index' }, :caption => 'Quickbooks'
|
||||
# set per_page globally
|
||||
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, :customers, { :controller => :customers, :action => :index }, :caption => 'Customers', :if => Proc.new { User.current.logged? }
|
||||
end
|
||||
|
||||
@@ -13,6 +13,7 @@ require_dependency 'issue'
|
||||
# Patches Redmine's Issues dynamically.
|
||||
# Adds a relationships
|
||||
module IssuePatch
|
||||
|
||||
def self.included(base) # :nodoc:
|
||||
base.extend(ClassMethods)
|
||||
|
||||
@@ -25,7 +26,9 @@ module IssuePatch
|
||||
belongs_to :qbo_item, primary_key: :id
|
||||
belongs_to :qbo_estimate, primary_key: :id
|
||||
belongs_to :qbo_invoice, primary_key: :id
|
||||
belongs_to :vehicle, primary_key: :id
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
||||
@@ -13,32 +13,36 @@ class IssuesFormHookListener < Redmine::Hook::ViewListener
|
||||
# Edit Issue Form
|
||||
# Show a dropdown for quickbooks contacts
|
||||
def view_issues_form_details_bottom(context={})
|
||||
# Update the customer and item database
|
||||
#QboCustomer.update_all
|
||||
#QboItem.update_all
|
||||
#QboInvoice.update_all
|
||||
#QboEstimate.update_all
|
||||
f = context[:form]
|
||||
|
||||
# Check to see if there is a quickbooks user attached to the issue
|
||||
@selected_customer = context[:issue].qbo_customer ? context[:issue].qbo_customer.id : nil
|
||||
@selected_item = context[:issue].qbo_item ? context[:issue].qbo_item.id : nil
|
||||
@selected_invoice = context[:issue].qbo_invoice ? context[:issue].qbo_invoice.id : nil
|
||||
@selected_estimate = context[:issue].qbo_estimate ? context[:issue].qbo_estimate.id : nil
|
||||
selected_customer = context[:issue].qbo_customer ? context[:issue].qbo_customer.id : nil
|
||||
selected_item = context[:issue].qbo_item ? context[:issue].qbo_item.id : nil
|
||||
selected_invoice = context[:issue].qbo_invoice ? context[:issue].qbo_invoice.id : nil
|
||||
selected_estimate = context[:issue].qbo_estimate ? context[:issue].qbo_estimate.id : nil
|
||||
|
||||
customer = QboCustomer.find_by_id(selected_customer) if selected_customer
|
||||
vehicle = context[:issue].vehicles_id
|
||||
|
||||
# Generate the drop down list of quickbooks customers
|
||||
@select_customer = context[:form].select :qbo_customer_id, QboCustomer.all.pluck(:name, :id).sort, :selected => selected_customer, include_blank: true
|
||||
select_customer = f.select :qbo_customer_id, QboCustomer.all.pluck(:name, :id).sort, :selected => selected_customer, include_blank: true
|
||||
|
||||
# Generate the drop down list of quickbooks items
|
||||
@select_item = context[:form].select :qbo_item_id, QboItem.all.pluck(:name, :id).sort, :selected => selected_item, include_blank: true
|
||||
select_item = f.select :qbo_item_id, QboItem.all.pluck(:name, :id).sort, :selected => selected_item, include_blank: true
|
||||
|
||||
# Generate the drop down list of quickbooks invoices
|
||||
@select_invoice = context[:form].select :qbo_invoice_id, QboInvoice.all.pluck(:doc_number, :id).sort! {|x, y| y <=> x}, :selected => selected_invoice, include_blank: true
|
||||
select_invoice = f.select :qbo_invoice_id, QboInvoice.all.pluck(:doc_number, :id).sort! {|x, y| y <=> x}, :selected => selected_invoice, include_blank: true
|
||||
|
||||
# Generate the drop down list of quickbooks extimates
|
||||
@select_estimate = context[:form].select :qbo_estimate_id, QboEstimate.all.pluck(:doc_number, :id).sort! {|x, y| y <=> x}, :selected => selected_estimate, include_blank: true
|
||||
select_estimate = f.select :qbo_estimate_id, QboEstimate.all.pluck(:doc_number, :id).sort! {|x, y| y <=> x}, :selected => selected_estimate, include_blank: true
|
||||
|
||||
#@estimates_link = link_to qbo_update_estimates_path
|
||||
|
||||
return "<p>#{@select_customer}</p> <p>#{@select_item}</p> <p>#{@select_invoice}</p> <p>#{@select_estimate} #{@estimates_link}</p>"
|
||||
#render_on :view_issues_form_details_bottom, :partial => 'hooks/redmine_qbo/vehicles/dropdown'
|
||||
vehicles = customer.vehicles.pluck(:name, :id).sort if customer
|
||||
vehicles = Vehicle.all if not vehicles
|
||||
vehicle = f.select :vehicles_id, vehicles, include_blank: true, :selected => vehicle
|
||||
|
||||
return "<p>#{select_customer}</p> <p>#{select_item}</p> <p>#{select_invoice}</p> <p>#{select_estimate}</p> <p>#{vehicle}</p>"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -21,46 +21,69 @@ class IssuesShowHookListener < Redmine::Hook::ViewListener
|
||||
issue = context[:issue]
|
||||
|
||||
# Check to see if there is a quickbooks user attached to the issue
|
||||
@customer = issue.qbo_customer ? issue.qbo_customer.name : nil
|
||||
customer = issue.qbo_customer ? issue.qbo_customer.name : nil
|
||||
|
||||
# Check to see if there is a quickbooks item attached to the issue
|
||||
@item = issue.qbo_item ? issue.qbo_item.name : nil
|
||||
item = issue.qbo_item ? issue.qbo_item.name : nil
|
||||
|
||||
@estimate = nil
|
||||
@estimate_link = nil
|
||||
# Estimate Number
|
||||
if issue.qbo_estimate
|
||||
@estimate = issue.qbo_estimate.doc_number
|
||||
@estimate_link = link_to @estimate, "#{Redmine::Utils::relative_url_root }/qbo/estimate/#{issue.qbo_estimate.id}", :target => "_blank"
|
||||
estimate = issue.qbo_estimate.doc_number
|
||||
estimate_link = link_to estimate, "#{Redmine::Utils::relative_url_root}/qbo/estimate/#{issue.qbo_estimate.id}", :target => "_blank"
|
||||
end
|
||||
|
||||
@invoice = nil
|
||||
@invo = nil
|
||||
# Invoice Number
|
||||
if issue.qbo_invoice
|
||||
@invoice = issue.qbo_invoice.doc_number
|
||||
@invoice_link = link_to @invoice, "#{Redmine::Utils::relative_url_root }/qbo/invoice/#{issue.qbo_invoice.id}", :target => "_blank"
|
||||
invoice = issue.qbo_invoice.doc_number
|
||||
invoice_link = link_to invoice, "#{Redmine::Utils::relative_url_root}/qbo/invoice/#{issue.qbo_invoice.id}", :target => "_blank"
|
||||
end
|
||||
|
||||
return "<div class=\"attributes\">
|
||||
|
||||
if issue.vehicles_id
|
||||
v = Vehicle.find_by_id(issue.vehicles_id)
|
||||
vehicle = link_to v.to_s, "#{Redmine::Utils::relative_url_root}/vehicles/#{v.id}"
|
||||
vin = v.vin
|
||||
notes = v.notes
|
||||
end
|
||||
|
||||
return "
|
||||
<div class=\"attributes\">
|
||||
|
||||
<div class=\"qbo_customer_id attribute\">
|
||||
<div class=\"label\"><span>Customer</span>:</div>
|
||||
<div class=\"value\">#{@customer}</div>
|
||||
<div class=\"value\">#{customer}</div>
|
||||
</div>
|
||||
|
||||
<div class=\"qbo_item_id attribute\">
|
||||
<div class=\"label\"><span>Item</span>:</div>
|
||||
<div class=\"value\">#{@item}</div>
|
||||
<div class=\"value\">#{item}</div>
|
||||
</div>
|
||||
|
||||
<div class=\"qbo_estimate_id attribute\">
|
||||
<div class=\"label\"><span>Estimate</span>:</div>
|
||||
<div class=\"value\">#{@estimate_link}</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 class=\"value\">#{invoice_link}</div>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
<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\">#{vin}</div>
|
||||
</div>
|
||||
|
||||
<div class=\"vehicle_notes attribute\">
|
||||
<div class=\"label\"><span>Notes</span>:</div>
|
||||
<div class=\"value\">#{notes}</div>
|
||||
</div>
|
||||
</div>"
|
||||
end
|
||||
|
||||
246
lib/pdf_patch.rb
Normal file
246
lib/pdf_patch.rb
Normal file
@@ -0,0 +1,246 @@
|
||||
require_dependency 'redmine/export/pdf'
|
||||
require_dependency 'redmine/export/pdf/issues_pdf_helper'
|
||||
|
||||
module IssuesPdfHelperPatch
|
||||
|
||||
def self.included(base)
|
||||
base.send(:include, InstanceMethods)
|
||||
base.class_eval do
|
||||
unloadable # Send unloadable so it will not be unloaded in development
|
||||
alias_method_chain :issue_to_pdf, :patch
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
def issue_to_pdf_with_patch(issue, assoc={})
|
||||
pdf = ::Redmine::Export::PDF::ITCPDF.new(current_language)
|
||||
pdf.set_title("#{issue.project} - #{issue.tracker} ##{issue.id}")
|
||||
pdf.alias_nb_pages
|
||||
pdf.footer_date = format_date(Date.today)
|
||||
pdf.add_page
|
||||
pdf.SetFontStyle('B',11)
|
||||
buf = "#{issue.project} - #{issue.tracker} ##{issue.id}"
|
||||
pdf.RDMMultiCell(190, 5, buf)
|
||||
pdf.SetFontStyle('',8)
|
||||
base_x = pdf.get_x
|
||||
i = 1
|
||||
issue.ancestors.visible.each do |ancestor|
|
||||
pdf.set_x(base_x + i)
|
||||
buf = "#{ancestor.tracker} # #{ancestor.id} (#{ancestor.status.to_s}): #{ancestor.subject}"
|
||||
pdf.RDMMultiCell(190 - i, 5, buf)
|
||||
i += 1 if i < 35
|
||||
end
|
||||
pdf.SetFontStyle('B',11)
|
||||
pdf.RDMMultiCell(190 - i, 5, issue.subject.to_s)
|
||||
pdf.SetFontStyle('',8)
|
||||
pdf.RDMMultiCell(190, 5, "#{format_time(issue.created_on)} - #{issue.author}")
|
||||
pdf.ln
|
||||
|
||||
left = []
|
||||
left << [l(:field_status), issue.status]
|
||||
left << [l(:field_priority), issue.priority]
|
||||
left << [l(:field_qbo_customer), issue.qbo_customer.name]
|
||||
left << [l(:field_assigned_to), issue.assigned_to] unless issue.disabled_core_fields.include?('assigned_to_id')
|
||||
#left << [l(:field_category), issue.category] unless issue.disabled_core_fields.include?('category_id')
|
||||
#left << [l(:field_fixed_version), issue.fixed_version] unless issue.disabled_core_fields.include?('fixed_version_id')
|
||||
|
||||
v = Vehicle.find_by_id(issue.vehicles_id)
|
||||
vehicle = v ? v.to_s : nil
|
||||
vin = v ? v.vin : nil
|
||||
notes = v ? v.notes : nil
|
||||
left << [l(:field_vehicles), vehicle]
|
||||
left << [l(:field_vin), vin]
|
||||
left << [l(:field_notes), notes]
|
||||
|
||||
right = []
|
||||
right << [l(:field_start_date), format_date(issue.start_date)] unless issue.disabled_core_fields.include?('start_date')
|
||||
right << [l(:field_due_date), format_date(issue.due_date)] unless issue.disabled_core_fields.include?('due_date')
|
||||
right << [l(:field_done_ratio), "#{issue.done_ratio}%"] unless issue.disabled_core_fields.include?('done_ratio')
|
||||
right << [l(:field_estimated_hours), l_hours(issue.estimated_hours)] unless issue.disabled_core_fields.include?('estimated_hours')
|
||||
right << [l(:label_spent_time), l_hours(issue.total_spent_hours)] if User.current.allowed_to?(:view_time_entries, issue.project)
|
||||
#right << [l(:field_notes), notes]
|
||||
|
||||
rows = left.size > right.size ? left.size : right.size
|
||||
while left.size < rows
|
||||
left << nil
|
||||
end
|
||||
while right.size < rows
|
||||
right << nil
|
||||
end
|
||||
|
||||
half = (issue.visible_custom_field_values.size / 2.0).ceil
|
||||
issue.visible_custom_field_values.each_with_index do |custom_value, i|
|
||||
(i < half ? left : right) << [custom_value.custom_field.name, show_value(custom_value, false)]
|
||||
end
|
||||
|
||||
if pdf.get_rtl
|
||||
border_first_top = 'RT'
|
||||
border_last_top = 'LT'
|
||||
border_first = 'R'
|
||||
border_last = 'L'
|
||||
else
|
||||
border_first_top = 'LT'
|
||||
border_last_top = 'RT'
|
||||
border_first = 'L'
|
||||
border_last = 'R'
|
||||
end
|
||||
|
||||
rows = left.size > right.size ? left.size : right.size
|
||||
rows.times do |i|
|
||||
heights = []
|
||||
pdf.SetFontStyle('B',9)
|
||||
item = left[i]
|
||||
heights << pdf.get_string_height(35, item ? "#{item.first}:" : "")
|
||||
item = right[i]
|
||||
heights << pdf.get_string_height(35, item ? "#{item.first}:" : "")
|
||||
pdf.SetFontStyle('',9)
|
||||
item = left[i]
|
||||
heights << pdf.get_string_height(60, item ? item.last.to_s : "")
|
||||
item = right[i]
|
||||
heights << pdf.get_string_height(60, item ? item.last.to_s : "")
|
||||
height = heights.max
|
||||
|
||||
item = left[i]
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.RDMMultiCell(35, height, item ? "#{item.first}:" : "", (i == 0 ? border_first_top : border_first), '', 0, 0)
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.RDMMultiCell(60, height, item ? item.last.to_s : "", (i == 0 ? border_last_top : border_last), '', 0, 0)
|
||||
|
||||
item = right[i]
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.RDMMultiCell(35, height, item ? "#{item.first}:" : "", (i == 0 ? border_first_top : border_first), '', 0, 0)
|
||||
pdf.SetFontStyle('',9)
|
||||
pdf.RDMMultiCell(60, height, item ? item.last.to_s : "", (i == 0 ? border_last_top : border_last), '', 0, 2)
|
||||
|
||||
pdf.set_x(base_x)
|
||||
end
|
||||
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.RDMCell(35+155, 5, l(:field_description), "LRT", 1)
|
||||
pdf.SetFontStyle('',9)
|
||||
|
||||
# Set resize image scale
|
||||
pdf.set_image_scale(1.6)
|
||||
text = textilizable(issue, :description,
|
||||
:only_path => false,
|
||||
:edit_section_links => false,
|
||||
:headings => false,
|
||||
:inline_attachments => false
|
||||
)
|
||||
pdf.RDMwriteFormattedCell(35+155, 5, '', '', text, issue.attachments, "LRB")
|
||||
|
||||
unless issue.leaf?
|
||||
truncate_length = (!is_cjk? ? 90 : 65)
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.RDMCell(35+155,5, l(:label_subtask_plural) + ":", "LTR")
|
||||
pdf.ln
|
||||
issue_list(issue.descendants.visible.sort_by(&:lft)) do |child, level|
|
||||
buf = "#{child.tracker} # #{child.id}: #{child.subject}".
|
||||
truncate(truncate_length)
|
||||
level = 10 if level >= 10
|
||||
pdf.SetFontStyle('',8)
|
||||
pdf.RDMCell(35+135,5, (level >=1 ? " " * level : "") + buf, border_first)
|
||||
pdf.SetFontStyle('B',8)
|
||||
pdf.RDMCell(20,5, child.status.to_s, border_last)
|
||||
pdf.ln
|
||||
end
|
||||
end
|
||||
|
||||
relations = issue.relations.select { |r| r.other_issue(issue).visible? }
|
||||
unless relations.empty?
|
||||
truncate_length = (!is_cjk? ? 80 : 60)
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.RDMCell(35+155,5, l(:label_related_issues) + ":", "LTR")
|
||||
pdf.ln
|
||||
relations.each do |relation|
|
||||
buf = relation.to_s(issue) {|other|
|
||||
text = ""
|
||||
if Setting.cross_project_issue_relations?
|
||||
text += "#{relation.other_issue(issue).project} - "
|
||||
end
|
||||
text += "#{other.tracker} ##{other.id}: #{other.subject}"
|
||||
text
|
||||
}
|
||||
buf = buf.truncate(truncate_length)
|
||||
pdf.SetFontStyle('', 8)
|
||||
pdf.RDMCell(35+155-60, 5, buf, border_first)
|
||||
pdf.SetFontStyle('B',8)
|
||||
pdf.RDMCell(20,5, relation.other_issue(issue).status.to_s, "")
|
||||
pdf.RDMCell(20,5, format_date(relation.other_issue(issue).start_date), "")
|
||||
pdf.RDMCell(20,5, format_date(relation.other_issue(issue).due_date), border_last)
|
||||
pdf.ln
|
||||
end
|
||||
end
|
||||
pdf.RDMCell(190,5, "", "T")
|
||||
pdf.ln
|
||||
|
||||
if issue.changesets.any? &&
|
||||
User.current.allowed_to?(:view_changesets, issue.project)
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.RDMCell(190,5, l(:label_associated_revisions), "B")
|
||||
pdf.ln
|
||||
for changeset in issue.changesets
|
||||
pdf.SetFontStyle('B',8)
|
||||
csstr = "#{l(:label_revision)} #{changeset.format_identifier} - "
|
||||
csstr += format_time(changeset.committed_on) + " - " + changeset.author.to_s
|
||||
pdf.RDMCell(190, 5, csstr)
|
||||
pdf.ln
|
||||
unless changeset.comments.blank?
|
||||
pdf.SetFontStyle('',8)
|
||||
pdf.RDMwriteHTMLCell(190,5,'','',
|
||||
changeset.comments.to_s, issue.attachments, "")
|
||||
end
|
||||
pdf.ln
|
||||
end
|
||||
end
|
||||
|
||||
if assoc[:journals].present?
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.RDMCell(190,5, l(:label_history), "B")
|
||||
pdf.ln
|
||||
assoc[:journals].each do |journal|
|
||||
pdf.SetFontStyle('B',8)
|
||||
title = "##{journal.indice} - #{format_time(journal.created_on)} - #{journal.user}"
|
||||
title << " (#{l(:field_private_notes)})" if journal.private_notes?
|
||||
pdf.RDMCell(190,5, title)
|
||||
pdf.ln
|
||||
pdf.SetFontStyle('I',8)
|
||||
details_to_strings(journal.visible_details, true).each do |string|
|
||||
pdf.RDMMultiCell(190,5, "- " + string)
|
||||
end
|
||||
if journal.notes?
|
||||
pdf.ln unless journal.details.empty?
|
||||
pdf.SetFontStyle('',8)
|
||||
text = textilizable(journal, :notes,
|
||||
:only_path => false,
|
||||
:edit_section_links => false,
|
||||
:headings => false,
|
||||
:inline_attachments => false
|
||||
)
|
||||
pdf.RDMwriteFormattedCell(190,5,'','', text, issue.attachments, "")
|
||||
end
|
||||
pdf.ln
|
||||
end
|
||||
end
|
||||
|
||||
if issue.attachments.any?
|
||||
pdf.SetFontStyle('B',9)
|
||||
pdf.RDMCell(190,5, l(:label_attachment_plural), "B")
|
||||
pdf.ln
|
||||
for attachment in issue.attachments
|
||||
pdf.SetFontStyle('',8)
|
||||
pdf.RDMCell(80,5, attachment.filename)
|
||||
pdf.RDMCell(20,5, number_to_human_size(attachment.filesize),0,0,"R")
|
||||
pdf.RDMCell(25,5, format_date(attachment.created_on),0,0,"R")
|
||||
pdf.RDMCell(65,5, attachment.author.name,0,0,"R")
|
||||
pdf.ln
|
||||
end
|
||||
end
|
||||
pdf.output
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
Redmine::Export::PDF::IssuesPdfHelper.send(:include, IssuesPdfHelperPatch)
|
||||
62
lib/query_patch.rb
Normal file
62
lib/query_patch.rb
Normal file
@@ -0,0 +1,62 @@
|
||||
#The MIT License (MIT)
|
||||
#
|
||||
#Copyright (c) 2016 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 'issue_query'
|
||||
|
||||
module QueryPatch
|
||||
|
||||
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
|
||||
|
||||
alias_method_chain :available_columns, :qbo
|
||||
alias_method_chain :available_filters, :qbo
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
|
||||
def available_columns_with_qbo
|
||||
unless @available_columns
|
||||
@available_columns = available_columns_without_qbo
|
||||
@available_columns << QueryColumn.new(:qbo_customer, :sortable => "#{QboCustomer.table_name}.name", :groupable => true, :caption => :field_qbo_customer)
|
||||
end
|
||||
@available_columns
|
||||
end
|
||||
|
||||
def available_filters_with_qbo
|
||||
unless @available_filters
|
||||
@available_filters = available_filters_without_qbo
|
||||
|
||||
qbo_filters = {
|
||||
"customer" => {
|
||||
:name => l(:field_qbo_customer),
|
||||
:type => :text,
|
||||
:order => @available_filters.size + 1},
|
||||
}
|
||||
|
||||
@available_filters.merge!(qbo_filters)
|
||||
end
|
||||
@available_filters
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Add module to Issue
|
||||
IssueQuery.send(:include, QueryPatch)
|
||||
Reference in New Issue
Block a user