211 Commits
0.5.0 ... 0.8.1

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

View File

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

View File

@@ -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

@@ -4,9 +4,13 @@ A plugin for Redmine to connect to Quickbooks Online
The goal of this project is to allow Redmine to connect with Quickbooks Online to create `Time Activity Entries` for completed work when an Issue is closed.
`Note: Although the core functionality is complete, this project is still under heavy development. I am still working on refining everthing and adding other features. Tags should be stable`
#### Disclaimer
`Note: I am currently using this in a live production enviroment with no issues`
OAuth2 is hacked into place with version 0.8.0 & working but I'm sure I missed a few things
Note: Although the core functionality is complete, this project is still under heavy development. I am still working on refining everthing and adding other features. Tags should be stable
Also worth metioning I am currently using this in a live production enviroment with no issues
#### Features
* Issues can be assigned to a `Customer` via drop down in the edit Issue form
@@ -77,7 +81,7 @@ Note: After the inital synchronization, this plugin will recieve push notificati
The MIT License (MIT)
Copyright (c) 2017 rick barrette
Copyright (c) 2020 rick barrette
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@@ -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,94 @@
#The MIT License (MIT)
#
#Copyright (c) 2018 rick barrette
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
#
#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

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

View File

@@ -1,6 +1,6 @@
#The MIT License (MIT)
#
#Copyright (c) 2017 rick barrette
#Copyright (c) 2020 rick barrette
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
@@ -34,32 +34,48 @@ class QboController < ApplicationController
# Called when the user requests that Redmine to connect to QBO
#
def authenticate
callback = qbo_oauth_callback_url
token = Qbo.get_oauth_consumer.get_request_token(:oauth_callback => callback)
session[:qb_request_token] = Marshal.dump(token)
redirect_to("https://appcenter.intuit.com/Connect/Begin?oauth_token=#{token.token}") and return
oauth2_client = Qbo.get_client
callback = "https://redmine.rickbarrette.org/qbo/oauth_callback/"
#callback = qbo_oauth_callback_url
grant_url = oauth2_client.auth_code.authorize_url(redirect_uri: callback, response_type: "code", state: SecureRandom.hex(12), scope: "com.intuit.quickbooks.accounting")
redirect_to grant_url
end
#
# Called by QBO after authentication has been processed
#
def oauth_callback
at = Marshal.load(session[:qb_request_token]).get_access_token(:oauth_verifier => params[:oauth_verifier])
#There can only be one...
Qbo.destroy_all
# Save the authentication information
qbo = Qbo.new
qbo.qb_token = at.token
qbo.qb_secret = at.secret
qbo.token_expires_at = 6.months.from_now.utc
qbo.reconnect_token_at = 5.months.from_now.utc
qbo.company_id = params['realmId']
if qbo.save!
redirect_to qbo_sync_path, :flash => { :notice => "Successfully connected to Quickbooks" }
else
redirect_to plugin_settings_path(:redmine_qbo), :flash => { :error => "Error" }
if params[:state].present?
oauth2_client = Qbo.get_client
# use the state value to retrieve from your backend any information you need to identify the customer in your system
#redirect_uri = qbo_oauth_callback_url
redirect_uri = "https://redmine.rickbarrette.org/qbo/oauth_callback/"
if resp = oauth2_client.auth_code.get_token(params[:code], redirect_uri: redirect_uri)
# save your tokens here. For example:
# quickbooks_credentials.update_attributes(access_token: resp.token, refresh_token: resp.refresh_token,
# realm_id: params[:realmId])
Qbo.delete_all
# Save the authentication information
qbo = Qbo.new
qbo.qb_token = resp.token
qbo.qb_secret = resp.refresh_token
qbo.token_expires_at = 6.months.from_now.utc
qbo.reconnect_token_at = 3.months.from_now.utc
qbo.company_id = params[:realmId]
access_token = OAuth2::AccessToken.new(oauth2_client, resp.token, refresh_token: resp.refresh_token)
qbo.token = access_token.to_hash
qbo.expire = 1.hour.from_now.utc
if qbo.save!
redirect_to qbo_sync_path, :flash => { :notice => "Successfully connected to Quickbooks" }
else
redirect_to plugin_settings_path(:redmine_qbo), :flash => { :error => "Error" }
end
end
end
end
@@ -144,6 +160,6 @@ class QboController < ApplicationController
ActiveRecord::Base.connection.close
end
redirect_to qbo_path(:redmine_qbo), :flash => { :notice => "Successfully synced to Quickbooks" }
redirect_to :home, :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
@@ -49,7 +48,7 @@ class VehiclesController < ApplicationController
redirect_to @vehicle
else
flash[:error] = @vehicle.errors.full_messages.to_sentence
redirect_to new_vehicle_path
redirect_to Vehicle.find_by_vin @vehicle.vin
end
end
@@ -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
@@ -83,9 +81,10 @@ class VehiclesController < ApplicationController
flash[:notice] = "Vehicle updated"
redirect_to @vehicle
else
flash[:error] = @vehicle.errors.full_messages.to_sentence if @vehicle.errors
redirect_to edit_vehicle_path
end
#show any errors anyways
flash[:error] = @vehicle.errors.full_messages.to_sentence unless @vehicle.errors.empty?
rescue ActiveRecord::RecordNotFound
render_404
end

View File

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

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

@@ -0,0 +1,34 @@
#The MIT License (MIT)
#
#Copyright (c) 2018 rick barrette
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
#
#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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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,27 +19,32 @@ 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
#after_find :get_details
self.primary_key = :id
# 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
def details
get_details if @details.nil?
return @details
end
# returns the style of the vehicle
def style
get_details if @details.nil?
begin
return @details['years'][0]['styles'][0]['name'] if @details
return @details.trim if @details
rescue
return nil
end
@@ -49,21 +52,24 @@ 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
get_details if @details.nil?
return @details.doors if @details
end
# Force Upper Case for VIN numbers
# Force Upper Case for make numbers
def make=(val)
# The to_s is in case you get nil/non-string
write_attribute(:make, val.to_s.titleize)
end
# Force Upper Case for VIN numbers
# Force Upper Case for model numbers
def model=(val)
# The to_s is in case you get nil/non-string
write_attribute(:model, val.to_s.titleize)
@@ -71,44 +77,24 @@ 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
def self.search(search)
where("vin LIKE ?", "%#{search}%")
end
private
# init method to pull JSON details from Edmunds
def get_details
if self.vin?
begin
@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"
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 +102,24 @@ 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'
private
# init method to pull JSON details from Edmunds
def get_details
if self.vin?
#validate the vin before calling a remote server
validation = NhtsaVin.validate(self.vin)
begin
#if the vin validation failed, raise an exception and exit
raise RuntimeError, validation.error unless validation.valid?
# query NHTSA for details on the vin
query = NhtsaVin.get(self.vin)
raise RuntimeError, query.error unless query.valid?
@details = query.response
rescue Exception => e
errors.add(:vin, e.message)
end
end
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,14 @@
<div id="content">
<h2>Customer #<%= @customer.id %></h2>
<h2>Customer #<%= @customer.id %> - <%= @customer.name %> </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">
@@ -24,4 +22,3 @@
<h2>Issues:</h2>
<%= render :partial => 'issues/list_simple', locals: {issues: @issues} %>
</div>
</div>

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

View File

@@ -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

@@ -16,21 +16,6 @@
<td><%= @vin[0] if @vin %><b><%=@vin[1] if @vin%></b></td>
</tr>
<tr>
<th>Style</th>
<td><%= vehicle.style %></td>
</tr>
<tr>
<th>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 +30,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

View File

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

View File

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

View File

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

View File

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

23
init.rb
View File

@@ -1,6 +1,6 @@
#The MIT License (MIT)
#
#Copyright (c) 2017 rick barrette
#Copyright (c) 2020 rick barrette
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
@@ -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.8.1'
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

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

View File

@@ -21,6 +21,12 @@ 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
if context[:project]
selected_customer = context[:project].customer ? context[:project].customer.id : nil
selected_vehicle = context[:project].vehicle ? context[:project].vehicle.id : nil
end
# Check to see if there is a quickbooks user attached to the issue
selected_customer = context[:issue].customer ? context[:issue].customer.id : nil
@@ -33,7 +39,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

View File

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

View File

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

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_layouts_base_sidebar, :partial => "customers/sidebar"
end