Compare commits

...

330 Commits

Author SHA1 Message Date
ricky a34b6a07fc fixed typos 2017-03-23 05:56:26 -04:00
ricky 2ce811bbbf Update auth_helper.rb 2017-03-23 05:50:31 -04:00
ricky 02153de8b0 Added before filters add_customer, view_customer 2017-03-23 05:47:37 -04:00
ricky 68be20459b Added global_check_permission 2017-03-23 05:45:45 -04:00
ricky bbd03cc337 Update init.rb 2017-03-23 05:42:54 -04:00
ricky 4fc71a93f2 Update init.rb 2017-03-23 05:42:09 -04:00
ricky 8e7e1908e4 Update customers_controller.rb 2017-03-23 05:39:55 -04:00
ricky 89fba883ef Update customers_controller.rb 2017-03-23 05:38:06 -04:00
ricky 15f317fba1 Update customers_controller.rb 2017-03-23 05:36:51 -04:00
ricky 894ee9abfd added check_permission 2017-03-23 05:33:58 -04:00
ricky ca17807117 Update payments_controller.rb 2017-03-23 05:29:54 -04:00
ricky a70ba2f164 Update payments_controller.rb 2017-03-23 05:27:38 -04:00
ricky 78ac97298c Update payments_controller.rb 2017-03-23 05:25:57 -04:00
ricky 72cd349c1b Update payments_controller.rb 2017-03-23 05:23:44 -04:00
ricky 6fc1d27dca Update auth_helper.rb 2017-03-23 05:21:56 -04:00
ricky 525c6b99d6 Update auth_helper.rb 2017-03-23 05:19:13 -04:00
ricky 3eaff0ab30 Update auth_helper.rb 2017-03-23 05:14:47 -04:00
ricky 85b40bc9cf Update payments_controller.rb 2017-03-23 05:11:15 -04:00
ricky 37a2b95447 Update payments_controller.rb 2017-03-23 05:10:05 -04:00
ricky 33feb91713 added permission_checker 2017-03-23 05:08:33 -04:00
ricky f7357f30ce Update payments_controller.rb 2017-03-23 05:03:58 -04:00
ricky c0ae01018b Update payments_controller.rb 2017-03-23 05:01:01 -04:00
ricky 4353e910c8 Update payments_controller.rb 2017-03-23 04:57:22 -04:00
ricky bef9774c4e Update payments_controller.rb 2017-03-23 04:52:19 -04:00
ricky 863437b1b7 Added before filter to check permissions 2017-03-23 04:50:17 -04:00
ricky 7cfa15910a Update init.rb 2017-03-23 04:41:31 -04:00
ricky 2154a3d001 Update init.rb 2017-03-22 23:09:05 -04:00
ricky fdab090a3d Update init.rb 2017-03-22 23:06:12 -04:00
ricky 3f32b7fef1 Update init.rb 2017-03-22 22:53:21 -04:00
ricky 14422bc549 Update init.rb 2017-03-22 22:52:24 -04:00
ricky 6bb66597e8 Added some permissions
view_customers, add_customers, view_payments, add_payments
2017-03-22 22:44:09 -04:00
ricky 32b750b545 Version 0.4.2 2017-03-22 22:38:42 -04:00
ricky 5fd3141746 Merge pull request #7 from rickbarrette/dev
Removed un-needed js files
2017-03-22 22:26:04 -04:00
ricky 2c38361234 Removed un-needed js files 2017-03-22 22:20:12 -04:00
ricky 81b7b1492d Merge pull request #6 from rickbarrette/filter_vehicles_by_customer
Filter vehicles by customer
2017-03-22 22:07:22 -04:00
ricky 57ef1ac5a1 Fixed Typo 2017-03-22 22:05:33 -04:00
ricky 6597c5a13c Update application.js 2017-03-22 22:03:25 -04:00
ricky 8af97072fb Fixed filter_vehicles_by_customer method 2017-03-22 21:55:07 -04:00
ricky 48b6df0cef Update application.js 2017-03-22 21:34:04 -04:00
ricky 853b7ad804 Rename filter_vehicles_by_customerjs.erb to filter_vehicles_by_customer.js.erb 2017-03-22 21:27:15 -04:00
ricky 6a74baff5e Update application.js 2017-03-22 18:56:44 -04:00
ricky 8b21b0ff75 Update application.js 2017-03-22 18:54:47 -04:00
ricky d22a6303cd Update application.js 2017-03-22 18:53:47 -04:00
ricky 807d6643f4 Update application.js 2017-03-22 18:52:38 -04:00
ricky c725c2774c Update application.js 2017-03-22 18:50:21 -04:00
ricky ae0abae333 Update application.js 2017-03-22 18:48:52 -04:00
ricky fed2282212 Added debug 2017-03-22 18:37:06 -04:00
ricky 545960e676 Call autocomplete instead 2017-03-22 18:28:53 -04:00
ricky 66781f0625 Update application.js 2017-03-22 13:12:29 -04:00
ricky 504b9b93e4 Removed { 2017-03-22 13:11:19 -04:00
ricky b71bba473c Provide empty list 2017-03-22 13:09:15 -04:00
ricky aef3c453c4 Do not list all vehicles without customer 2017-03-22 13:04:50 -04:00
ricky 6de3ed94dc Update and rename app/views/filter_vehicles_by_customerjs.erb to app/views/customers/filter_vehicles_by_customerjs.erb
Also changed ID to select#issue_vehicles_id
2017-03-22 12:59:36 -04:00
ricky daada08ea7 Changed select to input 2017-03-22 12:48:43 -04:00
ricky fa37c98500 Removed ID 2017-03-22 12:47:08 -04:00
ricky 0ee59704b3 Fixed Javascript includes to += 2017-03-22 12:43:25 -04:00
ricky a22cbb9520 Added Javascript include for application.js 2017-03-22 11:09:19 -04:00
ricky c0d3f64d82 Create application.js 2017-03-22 11:08:14 -04:00
ricky 66d2bf4aa4 Create filter_vehicles_by_customerjs.erb 2017-03-22 11:04:48 -04:00
ricky 2b90c953ba Added filter_vehicles_by_customer method 2017-03-22 10:29:04 -04:00
ricky 0d5e5d679e Added route for filter_vehicles_by_customer 2017-03-22 10:24:16 -04:00
ricky 4da891cc07 Merge branch 'master' of git://github.com/sempervictus/redmine_qbo into sempervictus-master 2017-02-22 11:10:46 -05:00
ricky d3475a9b53 Delete vehicles.js 2017-02-14 14:42:13 -05:00
ricky eb583f78ec Delete update_vehicles.js.coffee 2017-02-14 14:41:55 -05:00
ricky 47bebf0a1a Delete application.js 2017-02-14 14:41:34 -05:00
ricky f9bd149f21 Commented out un-needed JS 2017-02-14 14:39:58 -05:00
ricky 5371b0f193 Update application.js 2017-01-29 22:40:48 -05:00
ricky 95497e5514 Update issues_form_hook_listener.rb 2017-01-29 22:40:02 -05:00
ricky 1a74abe76c Update issues_form_hook_listener.rb 2017-01-29 22:38:30 -05:00
ricky 7f11d3cdc4 Update issues_form_hook_listener.rb 2017-01-29 22:36:32 -05:00
ricky da01d79325 Update customers_controller.rb 2017-01-29 22:29:15 -05:00
ricky 82807cfede Update customers_controller.rb 2017-01-29 22:26:53 -05:00
ricky a268d10819 Update customers_controller.rb 2017-01-29 22:25:27 -05:00
ricky ba4bdd9ecb Update routes.rb 2017-01-29 22:19:12 -05:00
ricky 0a72e05e03 Update issues_form_hook_listener.rb 2017-01-29 22:05:29 -05:00
ricky 8d143ff06d Update customers_controller.rb 2017-01-29 22:00:19 -05:00
ricky 3ddb585a58 Update customers_controller.rb 2017-01-29 21:58:49 -05:00
ricky 1606ceb743 Update issues_form_hook_listener.rb 2017-01-29 21:49:57 -05:00
ricky 97578f8380 Update issues_form_hook_listener.rb 2017-01-29 21:47:09 -05:00
ricky 32beae70ef Update issues_form_hook_listener.rb 2017-01-29 21:45:13 -05:00
ricky 13fbc9e14f Update issues_form_hook_listener.rb 2017-01-29 21:44:00 -05:00
ricky c57a45c85e Update issues_form_hook_listener.rb 2017-01-29 21:43:25 -05:00
ricky 3711a9ca43 Update issues_form_hook_listener.rb 2017-01-29 21:38:18 -05:00
ricky d0c1693f38 Create autocomplete-rails.js 2017-01-29 21:37:27 -05:00
ricky 36534ee129 Update issues_form_hook_listener.rb 2017-01-29 21:34:28 -05:00
ricky 188c460054 Update issues_form_hook_listener.rb 2017-01-29 21:15:29 -05:00
ricky d9e3cb096b Update application.js 2017-01-29 21:10:41 -05:00
ricky b57b19493f Add files via upload 2017-01-29 21:08:29 -05:00
ricky 8c569db541 Update issues_form_hook_listener.rb 2017-01-29 20:56:30 -05:00
ricky 997257f42d Update issues_form_hook_listener.rb 2017-01-29 20:54:52 -05:00
ricky f9f1af17bc Update issues_form_hook_listener.rb 2017-01-29 20:53:09 -05:00
ricky f1f44d0048 Update issues_form_hook_listener.rb 2017-01-29 20:46:39 -05:00
ricky 7e8511090d Update issues_form_hook_listener.rb 2017-01-29 20:42:18 -05:00
ricky 4ca6a3138b Update issues_form_hook_listener.rb 2017-01-29 20:41:38 -05:00
ricky 21e1132e0e Update issues_form_hook_listener.rb 2017-01-29 20:36:19 -05:00
ricky dc15424014 Update issues_form_hook_listener.rb 2017-01-29 20:30:34 -05:00
ricky f90cdcd86b Update customers_controller.rb 2017-01-29 20:28:17 -05:00
ricky 5e53c18098 Update issues_form_hook_listener.rb 2017-01-29 20:24:06 -05:00
ricky 52d13ea7bc Update customers_controller.rb 2017-01-29 20:18:21 -05:00
ricky 5e24c5084e Update customers_controller.rb 2017-01-29 20:12:38 -05:00
ricky abdb61cc41 Update Gemfile 2017-01-29 20:07:08 -05:00
ricky 03556cc670 Update Gemfile 2017-01-29 20:03:53 -05:00
ricky 7f6cd99aba Update Gemfile 2017-01-29 19:58:11 -05:00
ricky ba513fb950 Update issues_form_hook_listener.rb 2017-01-29 19:47:24 -05:00
ricky 837ddd722c Update issues_form_hook_listener.rb 2017-01-29 19:46:21 -05:00
ricky f2b0cd3748 Update issues_form_hook_listener.rb 2017-01-29 19:45:31 -05:00
ricky f2a8878af4 Update issues_form_hook_listener.rb 2017-01-29 19:40:51 -05:00
ricky 13fccec54b Update routes.rb 2017-01-29 19:36:58 -05:00
ricky eca2b986a9 Update customers_controller.rb 2017-01-29 19:35:45 -05:00
ricky a06599b7f9 Update issues_form_hook_listener.rb 2017-01-29 19:34:31 -05:00
ricky 7fda4dc577 Create application.js 2017-01-29 19:32:59 -05:00
ricky 9e47152e12 Update Gemfile 2017-01-29 19:31:31 -05:00
ricky 83d21da41a Update issues_form_hook_listener.rb 2017-01-29 19:27:23 -05:00
ricky a692f03bfa Update init.rb 2017-01-29 19:17:37 -05:00
ricky 994cdf908f Update init.rb 2017-01-29 19:16:55 -05:00
ricky b022d17fc0 Update init.rb 2017-01-29 19:16:05 -05:00
ricky 644899c0b5 Update email_worker.rb 2017-01-29 19:15:14 -05:00
ricky be3a3b920d Update email_worker.rb 2017-01-27 12:04:58 -05:00
ricky d546eb026f Update email_worker.rb 2017-01-27 12:01:53 -05:00
ricky fdc59feb13 Create email_worker.rb 2017-01-27 11:47:30 -05:00
ricky 186b726a7b Added nil check 2017-01-27 09:38:00 -05:00
RageLtMan fa362bad55 Merge pull request #3 from sempervictus/bug-dont_call_string_method_on_nil_token
Do not permit OAUTH_CONSUMER_SECRET to be nil
2017-01-19 04:09:34 -05:00
RageLtMan fcf55bb504 Do not permit OAUTH_CONSUMER_SECRET to be nil
When QBO plugin is not configured, OAUTH_CONSUMER_SECRET can be
nil, and any codepath hitting the model raises a stack trace.

Set a "safe-ish" value here to allow execution in conditions where
QBO plugin is installed, but not yet configured.
2017-01-19 04:02:59 -05:00
RageLtMan 2185667665 Merge pull request #2 from sempervictus/local
Merge initial changes 20170103
2017-01-03 04:34:58 -05:00
RageLtMan 772483817e Prevent billing if issue has no customer assigned 2017-01-03 04:32:04 -05:00
RageLtMan 178ddd32c7 Remove will_paginate version constraint 2017-01-02 05:01:41 -05:00
ricky 08e047c90e Added done_ratio to partial billing 2016-10-12 10:31:05 -04:00
ricky b3c3314385 Remove Breaks 2016-09-26 16:41:41 -04:00
ricky 9fd5e01bb4 Update qbo_invoice.rb 2016-09-26 16:39:12 -04:00
ricky cd62f65fcd Update qbo_invoice.rb 2016-09-26 16:34:56 -04:00
ricky fb40833abd Update qbo_invoice.rb 2016-09-26 16:33:56 -04:00
ricky 6aae155933 Update qbo_invoice.rb 2016-09-21 19:51:22 -04:00
ricky f9e0ae8fef Update qbo_invoice.rb 2016-09-21 19:48:26 -04:00
ricky 489e335ca4 Update qbo_invoice.rb 2016-09-21 19:46:55 -04:00
ricky 874d0b4db9 Update qbo_invoice.rb 2016-09-21 19:43:40 -04:00
ricky 49e8f70b46 Update qbo_invoice.rb 2016-09-21 16:17:07 -04:00
ricky 77ea20171e Don't tie invoice to issue if customer is diffrent 2016-09-21 16:15:43 -04:00
ricky 11d4034c37 Update query_patch.rb 2016-09-19 23:19:53 -04:00
ricky 64369470de Update auth_helper.rb 2016-09-19 23:01:58 -04:00
ricky 7b483f3290 Update invoice_controller.rb 2016-09-19 23:01:27 -04:00
ricky 32bec79c28 Update invoice_controller.rb 2016-09-19 22:59:44 -04:00
ricky dfd9622ab7 Update invoice_controller.rb 2016-09-19 22:59:02 -04:00
ricky 334d3c930b Update auth_helper.rb 2016-09-19 22:57:19 -04:00
ricky 8cf2f370bf Update invoice_controller.rb 2016-09-19 22:54:58 -04:00
ricky 3965bed6c4 Update invoice_controller.rb 2016-09-19 22:50:18 -04:00
ricky 52396eb384 Update invoice_controller.rb 2016-09-19 22:49:06 -04:00
ricky 9cfab7bea1 Update invoice_controller.rb 2016-09-19 22:44:52 -04:00
ricky c8ef3bbd4e Update invoice_controller.rb 2016-09-19 22:38:48 -04:00
ricky 39e7d3c062 Update invoice_controller.rb 2016-09-19 22:37:53 -04:00
ricky 6fa96e11df Update invoice_controller.rb 2016-09-19 22:35:51 -04:00
ricky ecde64193a Update invoice_controller.rb 2016-09-19 22:32:30 -04:00
ricky f701af9a4d Update auth_helper.rb 2016-09-19 22:30:14 -04:00
ricky 6d99702a11 Update customers_controller.rb 2016-09-19 22:27:03 -04:00
ricky 138f8f2c2f Update auth_helper.rb 2016-09-19 22:21:37 -04:00
ricky 61ddf7378d Update auth_helper.rb 2016-09-19 22:18:19 -04:00
ricky f5b72f30be Update customers_controller.rb 2016-09-19 22:17:15 -04:00
ricky 1863b33955 Update view.html.erb 2016-09-19 22:05:48 -04:00
ricky a4573fce1c Update issue_patch.rb 2016-09-19 22:01:32 -04:00
ricky 0461801ee0 Update issues_form_hook_listener.rb 2016-09-19 21:29:28 -04:00
ricky 4c2eaac013 Update routes.rb 2016-09-19 21:26:40 -04:00
ricky 7ca56ccd2e Update routes.rb 2016-09-19 20:27:26 -04:00
ricky 915a59afa4 Update issues_form_hook_listener.rb 2016-09-19 20:17:00 -04:00
ricky ac61950d48 Update routes.rb 2016-09-19 20:13:57 -04:00
ricky b257fef563 Update customers_controller.rb 2016-09-19 20:10:11 -04:00
ricky 504c27c197 Update qbo_invoice.rb 2016-09-19 11:10:26 -04:00
ricky a7a5e2c731 Update qbo_invoice.rb 2016-09-19 11:07:33 -04:00
ricky 21d72dcc33 Update qbo_invoice.rb 2016-09-19 11:06:35 -04:00
ricky da7ba40e61 Added Private Note Scanning
Also removed redundant checks
2016-09-19 10:59:20 -04:00
ricky b4d6fc55ea Update customers_controller.rb 2016-09-19 07:57:49 -04:00
ricky 515b8feff7 Update attachments_controller_patch.rb 2016-09-19 07:44:03 -04:00
ricky 8bc05db033 Update init.rb 2016-09-19 07:30:07 -04:00
ricky 34cd6b08dc Create attachments_controller_patch.rb 2016-09-19 07:29:16 -04:00
ricky 41195dc095 Update show.html.erb 2016-09-18 22:55:12 -04:00
ricky 33a83c8f76 Update _details.html.erb 2016-09-18 22:53:54 -04:00
ricky 4f613d3fe1 Update show.html.erb 2016-09-18 22:52:48 -04:00
ricky 1c7cdec600 Update show.html.erb 2016-09-18 22:41:24 -04:00
ricky 7ae60c0e62 Update show.html.erb 2016-09-18 22:38:26 -04:00
ricky 5dd04925e0 Update show.html.erb 2016-09-18 22:37:23 -04:00
ricky 92eedbd4d3 Update show.html.erb 2016-09-18 22:32:45 -04:00
ricky 5545d72adf Update view.html.erb 2016-09-16 23:11:59 -04:00
ricky 226d44cd28 Update customers_controller.rb 2016-09-16 23:10:21 -04:00
ricky b7152d6124 Update view.html.erb 2016-09-16 23:07:05 -04:00
ricky f3e9b58c87 Update customers_controller.rb 2016-09-16 23:06:20 -04:00
ricky 5209315236 Update view.html.erb 2016-09-16 23:05:23 -04:00
ricky f38a9e1ff0 Update view.html.erb 2016-09-16 23:04:47 -04:00
ricky 80fb296e24 Update customers_controller.rb 2016-09-16 23:03:07 -04:00
ricky 9dda339a32 Version Bump 0.4.1
* Various Bug Fixes
* Added a share button that creates a public view-able link for an issue that lasts 24 Hours
2016-09-15 07:32:38 -04:00
ricky 1098accc8a Bug Fix #2 2016-09-15 07:06:52 -04:00
ricky 12826cf436 Bug Fix #2 2016-09-15 07:05:19 -04:00
ricky f9f77fdcb1 Bug Fix #2 2016-09-15 07:04:50 -04:00
ricky 0bc935d3dd Bug Fix #2 2016-09-15 07:03:54 -04:00
ricky fc40e4a6fe Update README.md 2016-09-14 17:04:33 -04:00
ricky 99b658a03d Update qbo_invoice.rb 2016-09-14 16:49:54 -04:00
ricky dacde1e050 Update qbo_invoice.rb 2016-09-14 14:34:50 -04:00
ricky 365219eddb Update issues_form_hook_listener.rb 2016-09-14 09:31:22 -04:00
ricky bc4dbbadbb Update issues_form_hook_listener.rb 2016-09-14 09:30:07 -04:00
ricky c06d2300f2 Update vehicles.js 2016-09-14 09:25:05 -04:00
ricky 5e34587a53 Update issues_form_hook_listener.rb 2016-09-14 09:24:04 -04:00
ricky eb39b297f9 Update and rename vehicles.js.coffee to vehicles.js 2016-09-14 09:23:38 -04:00
ricky 798a7c9933 Update issues_form_hook_listener.rb 2016-09-14 09:00:23 -04:00
ricky 9dee336e76 Rename update_vehicles.coffee.js to update_vehicles.js.coffee 2016-09-14 08:59:22 -04:00
ricky 9ba43d63b9 Rename vehicles.coffee.js to vehicles.js.coffee 2016-09-14 08:59:05 -04:00
ricky 8126671df9 Update vehicles.coffee.js 2016-09-14 08:54:29 -04:00
ricky 3f0ccd79f3 Update vehicles.coffee.js 2016-09-14 08:47:56 -04:00
ricky e61023acd5 Update issues_form_hook_listener.rb 2016-09-14 08:43:05 -04:00
ricky 35c2e7a951 Update issues_form_hook_listener.rb 2016-09-14 08:42:11 -04:00
ricky 81e8a9594f Update issues_form_hook_listener.rb 2016-09-14 08:40:10 -04:00
ricky 63415f8e58 Delete autocomplete.js 2016-09-14 08:37:15 -04:00
ricky ed9b1ea7b9 Update issues_form_hook_listener.rb 2016-09-14 08:36:54 -04:00
ricky 6026f9cdfc Update issues_form_hook_listener.rb 2016-09-14 08:31:30 -04:00
ricky 1924156a8c Rename vehicles.js.coffee to vehicles.coffee.js 2016-09-14 08:30:19 -04:00
ricky 3927b2b007 Rename update_vehicles.js.coffee to update_vehicles.coffee.js 2016-09-14 08:29:54 -04:00
ricky 7a2e984df7 Update issues_form_hook_listener.rb 2016-09-14 00:31:06 -04:00
ricky 2c9559104a Update issues_form_hook_listener.rb 2016-09-14 00:10:40 -04:00
ricky 07e342845a Update Gemfile 2016-09-14 00:04:46 -04:00
ricky dbab5bfbca Update issues_form_hook_listener.rb 2016-09-13 23:58:32 -04:00
ricky cc70c95115 Create update_vehicles.js.coffee 2016-09-13 23:57:12 -04:00
ricky 03bcff2b9a Create vehicles.js.coffee 2016-09-13 23:55:54 -04:00
ricky 9c4ed3d9e1 Update vehicles_controller.rb 2016-09-13 23:43:06 -04:00
ricky 8156657eb2 Update issues_show_hook_listener.rb 2016-09-13 23:37:27 -04:00
ricky c2ffedc8de Update issues_show_hook_listener.rb 2016-09-13 23:33:48 -04:00
ricky 3600c3e80a Update issues_show_hook_listener.rb 2016-09-13 23:32:45 -04:00
ricky 2970bd092c Update issues_show_hook_listener.rb 2016-09-13 23:28:36 -04:00
ricky a2cac188bb Update issues_show_hook_listener.rb 2016-09-13 23:26:37 -04:00
ricky e542f098a8 Update issues_show_hook_listener.rb 2016-09-13 23:24:34 -04:00
ricky 04a1670ac2 Update qbo_invoice.rb 2016-09-13 23:17:51 -04:00
ricky c189bc5dca Update qbo_invoice.rb 2016-09-13 23:14:18 -04:00
ricky 4b7cf407e8 Update qbo_invoice.rb 2016-09-13 22:57:45 -04:00
ricky 332deed21e Update qbo_invoice.rb 2016-09-13 22:56:36 -04:00
ricky 41313029cd Update qbo_invoice.rb 2016-09-13 22:54:52 -04:00
ricky bbc3b138cf Update qbo_invoice.rb 2016-09-13 22:48:11 -04:00
ricky e4d5770bdc Update qbo_invoice.rb 2016-09-13 22:44:12 -04:00
ricky 53a0a47dd6 Update qbo_invoice.rb 2016-09-13 22:41:51 -04:00
ricky 48edc85e2c Update qbo_invoice.rb 2016-09-13 22:39:25 -04:00
ricky c685aaa245 Update qbo_invoice.rb 2016-09-13 22:30:52 -04:00
ricky 2a79389b18 Update qbo_invoice.rb 2016-09-13 22:23:36 -04:00
ricky c3e4d0dbc2 Update invoice_controller.rb 2016-09-06 23:33:39 -04:00
ricky 89123fed31 Update header_footer_hook_listener.rb 2016-09-06 23:05:24 -04:00
ricky 571811ace6 Update header_footer_hook_listener.rb 2016-09-06 23:01:26 -04:00
ricky cb24967713 Update qbo_invoice.rb 2016-09-06 19:09:59 -04:00
ricky 449a59188c Update qbo_invoice.rb 2016-09-06 19:02:32 -04:00
ricky 907448ce3e Update qbo_invoice.rb 2016-09-06 19:01:27 -04:00
ricky 4f08af3987 Update _form.html.erb 2016-09-06 11:00:07 -05:00
ricky 00285e1f24 Update vehicle.rb 2016-09-06 10:56:30 -05:00
ricky 5b56d7a878 Update customers_controller.rb 2016-09-05 20:40:32 -04:00
ricky 5c0d1def9f Update customers_controller.rb 2016-09-05 20:35:10 -04:00
ricky fc3e252fff Update customers_controller.rb 2016-09-05 20:33:27 -04:00
ricky d605f617e4 Update issues_show_hook_listener.rb 2016-09-05 19:58:40 -04:00
ricky eb390e09d8 Update issues_show_hook_listener.rb 2016-09-05 19:57:17 -04:00
ricky bde29ef9d0 Update issue_patch.rb 2016-09-05 19:54:13 -04:00
ricky 5ffc9ed01c Update issue_patch.rb 2016-09-05 19:53:06 -04:00
ricky c992370962 Update issues_show_hook_listener.rb 2016-09-05 19:49:17 -04:00
ricky b3acf9f29d Update customers_controller.rb 2016-09-05 19:27:03 -04:00
ricky dca3735ce1 Update customers_controller.rb 2016-09-05 19:25:32 -04:00
ricky c7a5c1147f Update customers_controller.rb 2016-09-03 23:50:24 -04:00
ricky 8940e72091 Update customers_controller.rb 2016-09-03 23:48:54 -04:00
ricky 1ed7c6fe63 Update customers_controller.rb 2016-09-03 23:47:05 -04:00
ricky a197dcdefc Update README.md 2016-09-03 23:31:11 -04:00
ricky e00f73a48d Update en.yml 2016-09-03 09:53:26 -04:00
ricky 1c977a6687 Update query_patch.rb 2016-09-03 09:37:27 -04:00
ricky b3e93bb465 Update time_entry_query_patch.rb 2016-09-03 09:35:23 -04:00
ricky 628c798238 Update init.rb 2016-09-03 09:34:17 -04:00
ricky b34bd9dd7c Update en.yml 2016-09-03 09:32:09 -04:00
ricky 4d40093fe9 Update time_entry_query_patch.rb 2016-09-03 09:31:38 -04:00
ricky e573da2c11 Update init.rb 2016-09-03 09:30:52 -04:00
ricky f47316efbe Create time_entry_query_patch.rb 2016-09-03 09:28:51 -04:00
ricky f57c3c3df0 Update issue_patch.rb 2016-09-03 09:17:55 -04:00
ricky 9b91e4fd63 Update issue_patch.rb 2016-09-03 09:16:13 -04:00
ricky a264e707a8 Update issue_patch.rb 2016-09-03 09:15:37 -04:00
ricky a75a784e8d Update customers_controller.rb 2016-09-03 09:06:52 -04:00
ricky 1e04b6ae9f Update view.html.erb 2016-09-02 11:40:18 -04:00
ricky 6dfbfccced Update view.html.erb 2016-09-02 11:38:13 -04:00
ricky 27288c2eb2 Update view.html.erb 2016-09-02 11:36:59 -04:00
ricky 53a1be9761 Update view.html.erb 2016-09-02 11:28:06 -04:00
ricky d1c6492ea3 Update view.html.erb 2016-09-02 11:26:51 -04:00
ricky 0f72d88c71 Update customers_controller.rb 2016-09-02 11:22:05 -04:00
ricky 9edfcecdaa Update customers_controller.rb 2016-09-02 11:20:31 -04:00
ricky 5303b3dfef Update view.html.erb 2016-09-02 11:18:42 -04:00
ricky 1213c2e57a Update view.html.erb 2016-09-02 11:17:54 -04:00
ricky 49ca69aa78 Update view.html.erb 2016-09-02 11:17:22 -04:00
ricky 586f7c8fb9 Update view.html.erb 2016-09-02 11:16:40 -04:00
ricky 7a68fcfa92 Update customers_controller.rb 2016-09-02 11:15:18 -04:00
ricky 357e5d4490 Update customers_controller.rb 2016-09-02 11:14:01 -04:00
ricky af25326c23 Update view.html.erb 2016-09-02 11:09:12 -04:00
ricky e1db312982 Update view.html.erb 2016-09-02 11:04:46 -04:00
ricky 59a418727e Update view.html.erb 2016-09-02 11:04:36 -04:00
ricky c3d9833acb Update view.html.erb 2016-09-02 11:03:48 -04:00
ricky 59ebeb48ce Update view.html.erb 2016-09-02 11:03:10 -04:00
ricky 7bd23e993e Update view.html.erb 2016-09-02 11:01:54 -04:00
ricky 9b15f3f4f6 Update view.html.erb 2016-09-02 11:00:40 -04:00
ricky f087d3c6c0 Update view.html.erb 2016-09-02 10:59:55 -04:00
ricky 806b4719fe Update customers_controller.rb 2016-09-02 10:54:58 -04:00
ricky 126f4abe0a Update customer_token.rb 2016-09-02 10:52:19 -04:00
ricky 5ec76737b3 Update customer_token.rb 2016-09-02 10:50:32 -04:00
ricky 1d7bcc24fe Update issue_patch.rb 2016-09-02 10:46:37 -04:00
ricky 76a6fce406 Update customer_token.rb 2016-09-02 10:43:50 -04:00
ricky 3d44bcb04d Update customers_controller.rb 2016-09-02 10:40:15 -04:00
ricky fd8b5c280c Create view.html.erb 2016-09-02 10:39:43 -04:00
ricky ef6f104d5f Update customers_controller.rb 2016-09-02 10:34:51 -04:00
ricky 92538a58e3 Update customers_controller.rb 2016-09-02 10:34:09 -04:00
ricky 44bc2f47f1 Update customers_controller.rb 2016-09-02 10:32:58 -04:00
ricky 3c9316340f Update routes.rb 2016-09-02 10:31:05 -04:00
ricky 3ef9236388 Update customers_controller.rb 2016-09-02 10:29:48 -04:00
ricky dfdde631f9 Update routes.rb 2016-09-02 10:29:26 -04:00
ricky 372a6a1b6a Update auth_helper.rb 2016-09-02 10:28:17 -04:00
ricky 5a4996abac Update qbo_controller.rb 2016-09-02 10:10:55 -04:00
ricky 416df8d3f1 Update auth_helper.rb 2016-09-02 00:29:47 -04:00
ricky 0aa7fe8e73 Update auth_helper.rb 2016-09-02 00:25:32 -04:00
ricky f14e82a01b Update auth_helper.rb 2016-09-02 00:24:59 -04:00
ricky 5a10065bb0 Update auth_helper.rb 2016-09-02 00:23:49 -04:00
ricky fb801e9260 Update auth_helper.rb 2016-09-02 00:21:52 -04:00
ricky 0fa31f815e Update auth_helper.rb 2016-09-02 00:11:01 -04:00
ricky 76bd0d4e08 Update customer_token.rb 2016-09-02 00:04:33 -04:00
ricky b31c3ad550 Update customer_token.rb 2016-09-02 00:02:39 -04:00
ricky af7c1b0130 Update customer_token.rb 2016-09-02 00:00:48 -04:00
ricky 224b0b4238 Update 023_create_customer_tokens.rb 2016-09-01 23:57:24 -04:00
ricky e6fee6bd97 Update customer_token.rb 2016-09-01 23:52:39 -04:00
ricky 731b811cfe Update customer_token.rb 2016-09-01 23:48:48 -04:00
ricky 63d969c844 Added Customer Token Model 2016-09-01 23:45:51 -04:00
ricky 1138b0d5c9 Rename 21_add_issues_qbo_invoices.rb to 021_add_issues_qbo_invoices.rb 2016-09-01 23:37:07 -04:00
ricky 758810135d Rename 20_update_qbos_time_stamp.rb to 020_update_qbos_time_stamp.rb 2016-09-01 23:36:53 -04:00
ricky 6eff61b19d Create 022_update_issues_remove_invoice.rb 2016-09-01 23:36:40 -04:00
ricky b3bc17f327 Update Gemfile 2016-09-01 23:30:58 -04:00
ricky 67d4ac0ebf Update init.rb 2016-09-01 23:30:25 -04:00
ricky 11d3a2d0bf Update init.rb 2016-09-01 23:28:34 -04:00
ricky 1f76333af7 Update init.rb 2016-09-01 23:27:26 -04:00
ricky 816daeb429 Update init.rb 2016-09-01 23:24:28 -04:00
ricky b1bc19fb7a Update init.rb 2016-09-01 23:23:20 -04:00
ricky 578258f9e2 Update Gemfile 2016-09-01 23:19:14 -04:00
ricky 41fe8f6a5d Update issues_show_hook_listener.rb 2016-09-01 12:13:06 -04:00
39 changed files with 664 additions and 163 deletions
+8 -2
View File
@@ -6,5 +6,11 @@ gem 'oauth-plugin'
gem 'oauth' gem 'oauth'
gem 'roxml' gem 'roxml'
gem 'edmunds_vin' gem 'edmunds_vin'
gem 'will_paginate', '~> 3.1.0' gem 'will_paginate'
gem 'rails4-autocomplete' gem 'rails-jquery-autocomplete'
gem 'jquery-rails', '~> 3.1.4'
gem 'jquery-ui-rails'
group :assets do
gem 'coffee-rails'
end
+9 -13
View File
@@ -6,23 +6,22 @@ The goal of this project is to allow Redmine to connect with Quickbooks Online t
`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` `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`
`Note: I am currently using this in a live production enviroment with no issues`
####Features ####Features
* Issues can be assigned to a `Customer` via drop down in the edit Issue form * Issues can be assigned to a `Customer` via drop down in the edit Issue form
* The `Employee` for the Issue is assigned via the assigned Redmine User * The `Employee` for the Issue is assigned via the assigned Redmine User
- This is set via a drop down in the user admistration page. - This is set via a drop down in the user admistration page.
* IF an `Issue` has been assined a `Customer` when an Issue is closed the following will happen: * IF an `Issue` has been assined a `Customer` when an Issue is closed the following will happen:
- A new `Time Activity` will be created for the `Customer` assinged to the issue for each Redmine Time Entery. - A new `Time Activity` will be billed agaist the `Customer` assinged to the issue for each Redmine Time Entery.
+ Time Entries will be totalled up by Activity name. This will allow billing for diffrent activities without having to create seperate Issues. + Time Entries will be totalled up by Activity name. This will allow billing for diffrent activities without having to create seperate Issues.
+ The Time Activity names are used to lookup `Items` in Quickbooks. + The Time Activity names are used to lookup `Items` in Quickbooks.
+ IF there isn'tany Items that match the Activity name it will be skipped, and will not be billed to the `Customer` + IF there isn'tany Items that match the Activity name it will be skipped, and will not be billed to the `Customer`
- Labor Rates are set by the `Item` in Quickbooks - Labor Rates are set by the `Item` in Quickbooks
* `Issues` with the Tracker `Quote` will generate an estimate based on the estimated hours and `Item` rates.
- Needs to have a `Customer` Assiged
* `Payments` Can be created via the Redmine application menu * `Payments` Can be created via the Redmine application menu
* `Customers` Can be created via the Redmine application menu * `Customers` Can be created via the Redmine application menu
* `Customers` can be searched - `Customers` can be searched
* Basic information for the `Customer` can be viewed via the customer page - Basic information for the `Customer` can be viewed/edit via the Customer page
* `Custmoer` information can be update
* Webhook Support * Webhook Support
- `Invoices` are automaticly attached to an Issue if a line item has a hashtag number in a `Line Item` - `Invoices` are automaticly attached to an Issue if a line item has a hashtag number in a `Line Item`
+ `Invoice` Custom Fields are matched Issue Custom Fileds and are automaticly updated in Quickbooks. For example, this is usefull for extracting the Mileage In / Out from the Issue and updating the Invoice with the information. + `Invoice` Custom Fields are matched Issue Custom Fileds and are automaticly updated in Quickbooks. For example, this is usefull for extracting the Mileage In / Out from the Issue and updating the Invoice with the information.
@@ -47,30 +46,27 @@ The goal of this project is to allow Redmine to connect with Quickbooks Online t
3. Navigate to the plugin configuration page and suppy your own OAuth key & secret. 3. Navigate to the plugin configuration page and suppy your own OAuth key & secret.
![Alt plugin_config](/Screenshots/plugin_config.png)
4. After saving your key & secret, you need to click on the Authenticate link on the plugin configuration page to authenticate with QBO. 4. After saving your key & secret, you need to click on the Authenticate link on the plugin configuration page to authenticate with QBO.
5. Assign an Employee to each of your users via the User Administration Page 5. Assign an Employee to each of your users via the User Administration Page
![Alt plugin_user_edit](/Screenshots/plugin_user_edit.png) ## Automatic Deploy
If you want the redmine server to be automaticly restarted after a git pull event add this hook to your git hook directory
https://gist.github.com/rickbarrette/3c999c7f37e321f9c60380de99e494f5
## Usage ## Usage
To enable automatic `Time Activity` entries for an Issue , you need only to assign a `Customer` to an Issue via drop downs in the issue creation/update form. To enable automatic `Time Activity` entries for an Issue , you need only to assign a `Customer` to an Issue via drop downs in the issue creation/update form.
![Alt plugin_issue-edit](/Screenshots/plugin_issue_edit.png)
Note: After the inital synchronization, this plugin will recieve push notifications via Intuit's webhook service. Note: After the inital synchronization, this plugin will recieve push notifications via Intuit's webhook service.
## TODO ## TODO
* Abiltiy to add line items to a ticket in a dynamic table so they can be added to the invoice upon closing of the issue * Abiltiy to add line items to a ticket in a dynamic table so they can be added to the invoice upon closing of the issue
* Customer Deletion * Customer Deletion
* Email Customer updates, provding a link that would: bypass the login page, go directly to the issue directing them to, and allow them to view only that issue. * Email Customer updates, provding a link that would: bypass the login page, go directly to the issue directing them to, and allow them to view only that issue.
* Add a rake file to create required Trackers or statuses required
* Add Setting for Sandbox Mode * Add Setting for Sandbox Mode
* Refactor Models prefixed with Qbo... * Refactor Models prefixed with Qbo...
* Allow multiple Invoices to be attached to an Issue
* Seperate Vehicles into a seperate plugin * Seperate Vehicles into a seperate plugin
* Make HTML Pretty * Make HTML Pretty
* Intergrate Customer Search into Redmine Search * Intergrate Customer Search into Redmine Search
+63 -1
View File
@@ -13,11 +13,32 @@ class CustomersController < ApplicationController
unloadable unloadable
include AuthHelper include AuthHelper
helper :issues
helper :journals
helper :projects
helper :custom_fields
helper :issue_relations
helper :watchers
helper :attachments
helper :queries
include QueriesHelper
helper :repositories
helper :sort
include SortHelper
helper :timelog
before_filter :require_user before_filter :add_customer, :only => :new
before_filter :view_customer, :except => :new
skip_before_filter :verify_authenticity_token, :check_if_login_required, :only => [:view]
default_search_scope :names default_search_scope :names
autocomplete :customer, :name, :full => false, :extra_data => [:id]
def filter_vehicles_by_customer
@filtered_vehicles = Vehicle.all.where(customer_id: params[:selected_customer])
end
# display a list of all customers # display a list of all customers
def index def index
if params[:search] if params[:search]
@@ -89,8 +110,49 @@ class CustomersController < ApplicationController
end end
end end
# Customer view for an issue
def view
User.current = User.find_by lastname: 'Anonymous'
@token = CustomerToken.where("token = ? and expires_at > ?", params[:token], Time.now)
@token = @token.first
if @token
session[:token] = @token.token
@issue = Issue.find @token.issue_id
@journals = @issue.journals.
preload(:details).
preload(:user => :email_address).
reorder(:created_on, :id).to_a
@journals.each_with_index {|j,i| j.indice = i+1}
@journals.reject!(&:private_notes?) unless User.current.allowed_to?(:view_private_notes, @issue.project)
Journal.preload_journals_details_custom_fields(@journals)
@journals.select! {|journal| journal.notes? || journal.visible_details.any?}
@journals.reverse! if User.current.wants_comments_in_reverse_order?
@changesets = @issue.changesets.visible.preload(:repository, :user).to_a
@changesets.reverse! if User.current.wants_comments_in_reverse_order?
@relations = @issue.relations.select {|r| r.other_issue(@issue) && r.other_issue(@issue).visible? }
@allowed_statuses = @issue.new_statuses_allowed_to(User.current)
@priorities = IssuePriority.active
@time_entry = TimeEntry.new(:issue => @issue, :project => @issue.project)
@relation = IssueRelation.new
else
render_403
end
end
private private
def add_customer
global_check_permission(:add_customers)
end
def view_customer
global_check_permission(:view_customers)
end
def only_one_non_zero?( array ) def only_one_non_zero?( array )
found_non_zero = false found_non_zero = false
array.each do |val| array.each do |val|
+3 -2
View File
@@ -12,13 +12,14 @@ class InvoiceController < ApplicationController
include AuthHelper include AuthHelper
before_filter :require_user before_filter :require_user, :unless => proc {|c| session[:token].nil? }
skip_before_filter :verify_authenticity_token, :check_if_login_required, :unless => proc {|c| session[:token].nil? }
# #
# Downloads and forwards the invoice pdf # Downloads and forwards the invoice pdf
# #
def show def show
base = QboInvoice.get_base.service base = QboInvoice.get_base
invoice = base.fetch_by_id(params[:id]) invoice = base.fetch_by_id(params[:id])
@pdf = base.pdf(invoice) @pdf = base.pdf(invoice)
send_data @pdf, filename: "invoice #{invoice.doc_number}.pdf", :disposition => 'inline', :type => "application/pdf" send_data @pdf, filename: "invoice #{invoice.doc_number}.pdf", :disposition => 'inline', :type => "application/pdf"
+8 -2
View File
@@ -12,7 +12,7 @@ class PaymentsController < ApplicationController
include AuthHelper include AuthHelper
before_filter :require_user before_filter :check_permissions
def new def new
@payment = Payment.new @payment = Payment.new
@@ -32,11 +32,17 @@ class PaymentsController < ApplicationController
else else
flash[:error] = @payment.errors.full_messages.to_sentence flash[:error] = @payment.errors.full_messages.to_sentence
redirect_to new_customer_path redirect_to new_customer_path
end end
end end
private private
def check_permissions
if !allowed_to?(:add_payments)
render :file => "public/401.html.erb", :status => :unauthorized, :layout =>true
end
end
def only_one_non_zero?( array ) def only_one_non_zero?( array )
found_non_zero = false found_non_zero = false
array.each do |val| array.each do |val|
+5 -1
View File
@@ -16,7 +16,7 @@ class QboController < ApplicationController
include AuthHelper include AuthHelper
before_filter :require_user, :except => :qbo_webhook before_filter :require_user, :except => :qbo_webhook
skip_before_filter :verify_authenticity_token, :check_if_login_required skip_before_filter :verify_authenticity_token, :check_if_login_required, :only => [:qbo_webhook]
# #
# Called when the QBO Top Menu us shown # Called when the QBO Top Menu us shown
@@ -66,8 +66,12 @@ class QboController < ApplicationController
# Manual Billing # Manual Billing
def bill def bill
i = Issue.find_by_id params[:id] i = Issue.find_by_id params[:id]
if i.customer
i.bill_time i.bill_time
redirect_to i, :flash => { :notice => "Successfully Billed #{i.customer.name}" } redirect_to i, :flash => { :notice => "Successfully Billed #{i.customer.name}" }
else
redirect_to i, :flash => { :error => "Cannot bill without a customer assigned" }
end
end end
# Quickbooks Webhook Callback # Quickbooks Webhook Callback
+1 -1
View File
@@ -103,7 +103,7 @@ class VehiclesController < ApplicationController
# returns a dynamic list of vehicles owned by a customer # returns a dynamic list of vehicles owned by a customer
def update_vehicles def update_vehicles
@vehicles = Customer.find_by_id(params[:customer_id].to_i).vehicles @vehicles = Customer.find_by(customer_id: params[:customer_id].to_i).vehicles
respond_to do |format| respond_to do |format|
format.html { render(:text => "not implemented") } format.html { render(:text => "not implemented") }
format.js format.js
+35
View File
@@ -11,8 +11,43 @@
module AuthHelper module AuthHelper
def require_user def require_user
return unless session[:token].nil?
if !User.current.logged? if !User.current.logged?
render :file => "public/401.html.erb", :status => :unauthorized, :layout =>true render :file => "public/401.html.erb", :status => :unauthorized, :layout =>true
end end
end end
def allowed_to?(action)
return false if User.current.nil?
project = Project.find(params[:project_id])
return false if project.nil?
return true if User.current.allowed_to?(action, project)
false
end
def check_permission(permission)
if !allowed_to?(permission)
render :file => "public/401.html.erb", :status => :unauthorized, :layout =>true
end
end
def global_check_permission(permission)
if !globaly_allowed_to?(permission)
render :file => "public/401.html.erb", :status => :unauthorized, :layout =>true
end
end
def globaly_allowed_to?( action)
return false if User.current.nil?
projects = Project.all
projects.each { |p|
if User.current.allowed_to?(action, p)
return true
end
}
false
end
end end
+23
View File
@@ -0,0 +1,23 @@
#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 CustomerToken < ActiveRecord::Base
unloadable
has_many :issues
attr_accessible :token, :expires_at, :issue_id
validates_presence_of :expires_at, :issue_id
before_create :generate_token
OAUTH_CONSUMER_SECRET = Setting.plugin_redmine_qbo['settingsOAuthConsumerSecret'] || 'CONFIGURE_QBO__' + SecureRandom.uuid
def generate_token
self.token = SecureRandom.base64(15).tr('+/=lIO0', OAUTH_CONSUMER_SECRET)
end
end
+82 -31
View File
@@ -12,14 +12,15 @@ class QboInvoice < ActiveRecord::Base
unloadable unloadable
has_and_belongs_to_many :issues has_and_belongs_to_many :issues
attr_accessible :doc_number attr_accessible :doc_number, :id
validates_presence_of :doc_number validates_presence_of :doc_number, :id
self.primary_key = :id self.primary_key = :id
def self.get_base def self.get_base
Qbo.get_base(:invoice) Qbo.get_base(:invoice).service
end end
# sync ALL the invoices
def self.sync def self.sync
last = Qbo.first.last_sync last = Qbo.first.last_sync
@@ -27,48 +28,83 @@ class QboInvoice < ActiveRecord::Base
query << " WHERE Metadata.LastUpdatedTime >= '#{last.iso8601}' " if last query << " WHERE Metadata.LastUpdatedTime >= '#{last.iso8601}' " if last
if count == 0 if count == 0
invoices = get_base.service.all invoices = get_base.all
else else
invoices = get_base.service.query() invoices = get_base.query()
end end
# Update the invoice table # Update the invoice table
invoices.each { | invoice | invoices.each { | invoice |
sync_by_id invoice.id process_invoice invoice
} }
end end
#sync by invoice ID
def self.sync_by_id(id) def self.sync_by_id(id)
#update the information in the database #update the information in the database
invoice = Qbo.get_base(:invoice).service.fetch_by_id(id) invoice = get_base.fetch_by_id(id)
qbo_invoice = find_or_create_by(id: invoice.id) process_invoice invoice
end
private
# Attach the invoice to the issue
def self.attach_to_issue(issue, invoice)
return if issue.nil?
# skip this issue if the issue customer is not the same as the invoice customer
return if issue.customer_id != invoice.customer_ref.value.to_i
# Load the invoice into the database
qbo_invoice = QboInvoice.find_or_create_by(id: invoice.id)
qbo_invoice.doc_number = invoice.doc_number qbo_invoice.doc_number = invoice.doc_number
qbo_invoice.id = invoice.id qbo_invoice.id = invoice.id
qbo_invoice.save! qbo_invoice.save!
is_changed = false unless issue.qbo_invoices.include?(qbo_invoice)
issue.qbo_invoices << qbo_invoice
issue.save!
end
compare_custom_fields(issue, invoice)
end
# processes the invoice into the system
def self.process_invoice(invoice)
# Check the private notes
if not invoice.private_note.nil?
invoice.private_note.scan(/#(\w+)/).flatten.each { |issue|
attach_to_issue(Issue.find_by_id(issue.to_i), invoice)
}
end
# Scan the line items for hashtags and attach to the applicable issues # Scan the line items for hashtags and attach to the applicable issues
invoice.line_items.each { |line| invoice.line_items.each { |line|
if line.description if line.description
line.description.scan(/#(\w+)/).flatten.each { |issue| line.description.scan(/#(\w+)/).flatten.each { |issue|
i = Issue.find_by_id(issue.to_i) attach_to_issue(Issue.find_by_id(issue.to_i), invoice)
begin }
i.qbo_invoices << QboInvoice.find_by_id(invoice.id.to_i)
i.save!
rescue
# do nothing, the reccord exists
end end
}
end
def self.compare_custom_fields(issue, invoice)
is_changed = false
# update the invoive custom fields with infomation from the work ticket if available # update the invoive custom fields with infomation from the work ticket if available
invoice.custom_fields.each { |cf| invoice.custom_fields.each { |cf|
# VIN
# TODO Add some hooks here
# VIN from the attached vehicle
begin begin
if cf.name.eql? "VIN" if cf.name.eql? "VIN"
vin = Vehicle.find(i.vehicles_id).vin vin = Vehicle.find(issue.vehicles_id).vin
break if vin.blank? break if vin.nil?
cf.string_value = vin if not cf.string_value.to_s.eql? vin if not cf.string_value.to_s.eql? vin
break cf.string_value = vin.to_s
is_changed = true
end
end end
rescue rescue
#do nothing #do nothing
@@ -76,9 +112,30 @@ class QboInvoice < ActiveRecord::Base
# Custom Values # Custom Values
begin begin
value = i.custom_values.find_by(custom_field_id: CustomField.find_by_name(cf.name).id) value = issue.custom_values.find_by(custom_field_id: CustomField.find_by_name(cf.name).id)
# Check to see if the value is blank...
if not value.value.to_s.blank? if not value.value.to_s.blank?
# Check to see if the value is diffrent
if not cf.string_value.to_s.eql? value.value.to_s if not cf.string_value.to_s.eql? value.value.to_s
# Use the lowest Milage
if cf.name.eql? "Mileage In"
if cf.string_value.to_i > value.value.to_i or cf.string_value.blank?
cf.string_value = value.value.to_s
is_changed = true
end
end
# Use the max milage
if cf.name.eql? "Mileage Out"
if cf.string_value.to_i < value.value.to_i or cf.string_value.blank?
cf.string_value = value.value.to_s
is_changed = true
end
end
# Everything else
cf.string_value = value.value.to_s cf.string_value = value.value.to_s
is_changed = true is_changed = true
end end
@@ -87,17 +144,11 @@ class QboInvoice < ActiveRecord::Base
# Nothing to do here, there is no match # Nothing to do here, there is no match
end end
} }
# TODO Add some hooks here
# Push updates # Push updates
Qbo.get_base(:invoice).service.update(invoice) if is_changed get_base.update(invoice) if is_changed
}
end
}
end end
def self.update(id)
invoice = get_base.service.fetch_by_id(id)
qbo_invoice = find_or_create_by(id: id)
qbo_invoice.doc_number = invoice.doc_number
qbo_invoice.save!
end
end end
+2 -2
View File
@@ -87,8 +87,8 @@ class Vehicle < ActiveRecord::Base
if self.vin? if self.vin?
begin begin
@details = JSON.parse get_decoder.full(self.vin) @details = JSON.parse get_decoder.full(self.vin)
raise @details['message'] if @details['status'] == "NOT_FOUND" raise @details['message'] if @details['status'].to_s.eql? "NOT_FOUND"
raise @details['message'] if @details['status'] == "BAD_REQUEST" raise @details['message'] if @details['status'].to_s.eql? "BAD_REQUEST"
rescue Exception => e rescue Exception => e
errors.add(:vin, e.message) errors.add(:vin, e.message)
end end
-5
View File
@@ -1,10 +1,5 @@
<table> <table>
<tbody> <tbody>
<tr>
<th>Customer</th>
<td><%= customer.name %></td>
</tr>
<tr> <tr>
<th>Email</th> <th>Email</th>
<td><%= customer.email %></td> <td><%= customer.email %></td>
@@ -0,0 +1 @@
$('select#issue_vehicles_id').html('<%= j options_from_collection_for_select(@filtered_vehicles, :id, :to_s) %>');
+1 -1
View File
@@ -24,5 +24,5 @@
<% end %> <% end %>
<div> <div>
<%= Customer.count %> Customers - <b>Last Sync: </b> <%= Qbo.last_sync %> <%= Customer.count %> Customers - <b>Last Sync: </b> <%= Qbo.last_sync if Qbo.exists? %>
</div> </div>
+27 -12
View File
@@ -1,12 +1,27 @@
<h1>Customer #<%= @customer.id %></h1> <div id="content">
<br/> <h2>Customer #<%= @customer.id %></h2>
<h2>Details:</h2> <br/>
<%= render :partial => 'customers/details', locals: {customer: @customer} %>
<br/> <div class="subject">
<h2>Vehicles:</h2> <div><h3><%= @customer.name %></h3></div>
<%= render :partial => 'vehicles/list' %> </div>
<%= button_to "New Vehicle", new_customer_vehicle_path(@customer), method: :get %>
<br/> <div class="attributes">
<br/>
<h2>Issues:</h2> <div class="splitcontent">
<%= render :partial => 'issues/list_simple', locals: {issues: @issues} %> <div class="splitcontentleft">
<h4>Details:</h4>
<%= render :partial => 'customers/details', locals: {customer: @customer} %>
</div>
<div class="splitcontentleft">
<h4>Vehicles:</h4>
<%= render :partial => 'vehicles/list' %>
<%= button_to "New Vehicle", new_customer_vehicle_path(@customer), method: :get %>
</div>
</div>
<br/>
<h2>Issues:</h2>
<%= render :partial => 'issues/list_simple', locals: {issues: @issues} %>
</div>
</div>
+109
View File
@@ -0,0 +1,109 @@
<h2><%= issue_heading(@issue) %></h2>
<div class="<%= @issue.css_classes %> details">
<%= avatar(@issue.author, :size => "50") %>
<div class="subject">
<%= render_issue_subject_with_tree(@issue) %>
This customer link expires in <%= distance_of_time_in_words(Time.now, @token.expires_at) %>
</div>
<p class="author">
<%= authoring @issue.created_on, @issue.author %>.
<% if @issue.created_on != @issue.updated_on %>
<%= l(:label_updated_time, time_tag(@issue.updated_on)).html_safe %>.
<% end %>
</p>
<div class="attributes">
<%= issue_fields_rows do |rows|
rows.left l(:field_status), @issue.status.name, :class => 'status'
rows.left l(:field_priority), @issue.priority.name, :class => 'priority'
unless @issue.disabled_core_fields.include?('assigned_to_id')
rows.left l(:field_assigned_to), avatar(@issue.assigned_to, :size => "14").to_s.html_safe + (@issue.assigned_to ? link_to_user(@issue.assigned_to) : "-"), :class => 'assigned-to'
end
unless @issue.disabled_core_fields.include?('category_id') || (@issue.category.nil? && @issue.project.issue_categories.none?)
rows.left l(:field_category), (@issue.category ? @issue.category.name : "-"), :class => 'category'
end
unless @issue.disabled_core_fields.include?('fixed_version_id') || (@issue.fixed_version.nil? && @issue.assignable_versions.none?)
rows.left l(:field_fixed_version), (@issue.fixed_version ? link_to_version(@issue.fixed_version) : "-"), :class => 'fixed-version'
end
unless @issue.disabled_core_fields.include?('start_date')
rows.right l(:field_start_date), format_date(@issue.start_date), :class => 'start-date'
end
unless @issue.disabled_core_fields.include?('due_date')
rows.right l(:field_due_date), format_date(@issue.due_date), :class => 'due-date'
end
unless @issue.disabled_core_fields.include?('done_ratio')
rows.right l(:field_done_ratio), progress_bar(@issue.done_ratio, :legend => "#{@issue.done_ratio}%"), :class => 'progress'
end
unless @issue.disabled_core_fields.include?('estimated_hours')
if @issue.estimated_hours.present? || @issue.total_estimated_hours.to_f > 0
rows.right l(:field_estimated_hours), issue_estimated_hours_details(@issue), :class => 'estimated-hours'
end
end
#if User.current.allowed_to_view_all_time_entries?(@project)
if @issue.total_spent_hours > 0
rows.right l(:label_spent_time), issue_spent_hours_details(@issue), :class => 'spent-time'
end
#end
end %>
<%= render_custom_fields_rows(@issue) %>
<%= call_hook(:view_issues_show_details_bottom, :issue => @issue) %>
</div>
<% if @issue.description? || @issue.attachments.any? -%>
<hr />
<% if @issue.description? %>
<div class="description">
<div class="contextual">
<%= link_to l(:button_quote), quoted_issue_path(@issue), :remote => true, :method => 'post', :class => 'icon icon-comment' if @issue.notes_addable? %>
</div>
<p><strong><%=l(:field_description)%></strong></p>
<div class="wiki">
<%= textilizable @issue, :description, :attachments => @issue.attachments %>
</div>
</div>
<% end %>
<%= link_to_attachments @issue, :thumbnails => true %>
<% end -%>
<%= call_hook(:view_issues_show_description_bottom, :issue => @issue) %>
<% if !@issue.leaf? || User.current.allowed_to?(:manage_subtasks, @project) %>
<hr />
<div id="issue_tree">
<div class="contextual">
<%= link_to_new_subtask(@issue) if User.current.allowed_to?(:manage_subtasks, @project) %>
</div>
<p><strong><%=l(:label_subtask_plural)%></strong></p>
<%= render_descendants_tree(@issue) unless @issue.leaf? %>
</div>
<% end %>
<% if @relations.present? || User.current.allowed_to?(:manage_issue_relations, @project) %>
<hr />
<div id="relations">
<%= render :partial => 'issues/relations' %>
</div>
<% end %>
</div>
<% if @changesets.present? %>
<div id="issue-changesets">
<h3><%=l(:label_associated_revisions)%></h3>
<%= render :partial => 'issues/changesets', :locals => { :changesets => @changesets} %>
</div>
<% end %>
<% if @journals.present? %>
<div id="history">
<h3><%=l(:label_history)%></h3>
<%= render :partial => 'issues/history', :locals => { :issue => @issue, :journals => @journals } %>
</div>
<% end %>
<% html_title "#{@issue.tracker.name} ##{@issue.id}: #{@issue.subject}" %>
+1 -1
View File
@@ -103,5 +103,5 @@ Note: You need to authenticate after saving your key and secret above
<br/> <br/>
<div> <div>
<b>Last Sync: </b> <%= Qbo.last_sync %> <%= link_to " Sync Now", qbo_sync_path %> <b>Last Sync: </b> <%= Qbo.last_sync if Qbo.exists? %> <%= link_to " Sync Now", qbo_sync_path %>
</div> </div>
+1 -1
View File
@@ -36,7 +36,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
<br/> <br/>
<div> <div>
<b>Last Sync: </b> <%= Qbo.last_sync %> <b>Last Sync: </b> <%= Qbo.last_sync if Qbo.exists? %>
</div> </div>
</body> </body>
+1 -1
View File
@@ -34,7 +34,7 @@
<div class="clearfix"> <div class="clearfix">
VIN: VIN:
<div class="input"> <div class="input">
<%= f.text_field :vin %> <%= f.text_field :vin , :autofocus => true %>
</div> </div>
</div> </div>
+18
View File
@@ -0,0 +1,18 @@
#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.
# This sidekiq worker class will handle emailing weekly time reports
class EmailWorker
include Sidekiq::Worker
def perform()
# email something
end
end
+9
View File
@@ -0,0 +1,9 @@
$(function() {
$("input#issue_customer_id").on("change", function() {
$.ajax({
url: "/filter_vehicles_by_customer",
type: "GET",
data: { selected_customer: $("input#issue_customer_id").val() }
});
});
});
+1
View File
@@ -0,0 +1 @@
!function(t){t.fn.railsAutocomplete=function(e){var a=function(){this.railsAutoCompleter||(this.railsAutoCompleter=new t.railsAutocomplete(this))};if(void 0!==t.fn.on){if(!e)return;return t(document).on("focus",e,a)}return this.live("focus",a)},t.railsAutocomplete=function(t){var e=t;this.init(e)},t.railsAutocomplete.options={showNoMatches:!0,noMatchesLabel:"no existing match"},t.railsAutocomplete.fn=t.railsAutocomplete.prototype={railsAutocomplete:"0.0.1"},t.railsAutocomplete.fn.extend=t.railsAutocomplete.extend=t.extend,t.railsAutocomplete.fn.extend({init:function(e){function a(t){return t.split(e.delimiter)}function i(t){return a(t).pop().replace(/^\s+/,"")}e.delimiter=t(e).attr("data-delimiter")||null,e.min_length=t(e).attr("data-min-length")||t(e).attr("min-length")||2,e.append_to=t(e).attr("data-append-to")||null,e.autoFocus=t(e).attr("data-auto-focus")||!1,t(e).autocomplete({appendTo:e.append_to,autoFocus:e.autoFocus,delay:t(e).attr("delay")||0,source:function(a,r){var n=this.element[0],o={term:i(a.term)};t(e).attr("data-autocomplete-fields")&&t.each(t.parseJSON(t(e).attr("data-autocomplete-fields")),function(e,a){o[e]=t(a).val()}),t.getJSON(t(e).attr("data-autocomplete"),o,function(){var a={};t.extend(a,t.railsAutocomplete.options),t.each(a,function(i,r){if(a.hasOwnProperty(i)){var n=t(e).attr("data-"+i);a[i]=n?n:r}}),0==arguments[0].length&&t.inArray(a.showNoMatches,[!0,"true"])>=0&&(arguments[0]=[],arguments[0][0]={id:"",label:a.noMatchesLabel}),t(arguments[0]).each(function(a,i){var r={};r[i.id]=i,t(e).data(r)}),r.apply(null,arguments),t(n).trigger("railsAutocomplete.source",arguments)})},change:function(e,a){if(t(this).is("[data-id-element]")&&""!==t(t(this).attr("data-id-element")).val()&&(t(t(this).attr("data-id-element")).val(a.item?a.item.id:"").trigger("change"),t(this).attr("data-update-elements"))){var i=t.parseJSON(t(this).attr("data-update-elements")),r=a.item?t(this).data(a.item.id.toString()):{};if(i&&""===t(i.id).val())return;for(var n in i){var o=t(i[n]);o.is(":checkbox")?null!=r[n]&&o.prop("checked",r[n]):o.val(a.item?r[n]:"").trigger("change")}}},search:function(){var t=i(this.value);return t.length<e.min_length?!1:void 0},focus:function(){return!1},select:function(i,r){if(r.item.value=r.item.value.toString(),-1!=r.item.value.toLowerCase().indexOf("no match")||-1!=r.item.value.toLowerCase().indexOf("too many results"))return t(this).trigger("railsAutocomplete.noMatch",r),!1;var n=a(this.value);if(n.pop(),n.push(r.item.value),null!=e.delimiter)n.push(""),this.value=n.join(e.delimiter);else if(this.value=n.join(""),t(this).attr("data-id-element")&&t(t(this).attr("data-id-element")).val(r.item.id).trigger("change"),t(this).attr("data-update-elements")){var o=r.item,l=-1!=r.item.value.indexOf("Create New")?!0:!1,u=t.parseJSON(t(this).attr("data-update-elements"));for(var s in u)"checkbox"===t(u[s]).attr("type")?o[s]===!0||1===o[s]?t(u[s]).attr("checked","checked"):t(u[s]).removeAttr("checked"):l&&o[s]&&-1==o[s].indexOf("Create New")||!l?t(u[s]).val(o[s]).trigger("change"):t(u[s]).val("").trigger("change")}var c=this.value;return t(this).bind("keyup.clearId",function(){t.trim(t(this).val())!=t.trim(c)&&(t(t(this).attr("data-id-element")).val("").trigger("change"),t(this).unbind("keyup.clearId"))}),t(e).trigger("railsAutocomplete.select",r),!1}}),t(e).trigger("railsAutocomplete.init")}}),t(document).ready(function(){t("input[data-autocomplete]").railsAutocomplete("input[data-autocomplete]")})}(jQuery);
-4
View File
@@ -1,4 +0,0 @@
//= require jquery
//= require jquery_ujs
//= require jquery-ui
//= require autocomplete-rails
+2
View File
@@ -19,3 +19,5 @@ en:
field_vehicles: "Vehicle" field_vehicles: "Vehicle"
field_vin: "VIN" field_vin: "VIN"
field_notes: "Notes" field_notes: "Notes"
field_qbo_billed: "Billed"
label_week: "Week"
+6 -1
View File
@@ -25,6 +25,9 @@ get 'qbo/invoice/:id', :to => 'invoice#show', as: :invoice
#manual billing #manual billing
get 'qbo/bill/:id', :to => 'qbo#bill', as: :bill get 'qbo/bill/:id', :to => 'qbo#bill', as: :bill
#customer issue view
get 'customers/view/:token', :to => 'customers#view', as: :view
#payments #payments
resources :payments resources :payments
@@ -32,11 +35,13 @@ resources :payments
post 'qbo/webhook', :to => 'qbo#qbo_webhook' post 'qbo/webhook', :to => 'qbo#qbo_webhook'
#ajax #ajax
get "update_vehicles" => 'vehicles#update_vehicles', as: 'update_vehicles' get 'filter_vehicles_by_customer' => 'customers#filter_vehicles_by_customer'
# Nest Vehicles under customers # Nest Vehicles under customers
resources :customers do resources :customers do
resources :vehicles resources :vehicles
get :autocomplete_customer_name, :on => :collection
get :autocomplete_customer_vehicles, :on => :collection
end end
#allow for just vehicles too #allow for just vehicles too
@@ -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 UpdateIssuesRemoveInvoice < ActiveRecord::Migration
def change
remove_reference :issues, :qbo_invoice
end
end
+19
View File
@@ -0,0 +1,19 @@
#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 CreateCustomerTokens < ActiveRecord::Migration
def change
create_table :customer_tokens do |t|
t.string :token
t.timestamp :expires_at
t.references :issue
end
end
end
+14 -13
View File
@@ -15,22 +15,20 @@ Redmine::Plugin.register :redmine_qbo do
require_dependency 'issues_save_hook_listener' require_dependency 'issues_save_hook_listener'
require_dependency 'issues_show_hook_listener' require_dependency 'issues_show_hook_listener'
require_dependency 'users_show_hook_listener' require_dependency 'users_show_hook_listener'
require_dependency 'header_footer_hook_listener.rb' require_dependency 'header_footer_hook_listener'
# Patches to the Redmine core. Will not work in development mode # Patches to the Redmine core. Will not work in development mode
require_dependency 'issue_patch' require_dependency 'issue_patch'
require_dependency 'user_patch' require_dependency 'user_patch'
require_dependency 'query_patch' require_dependency 'query_patch'
require_dependency 'time_entry_query_patch'
require_dependency 'pdf_patch' require_dependency 'pdf_patch'
require_dependency 'attachments_controller_patch'
Rails.configuration.to_prepare do
Redmine::Search.available_search_types << 'customers'
end
name 'Redmine Quickbooks Online plugin' name 'Redmine Quickbooks Online plugin'
author 'Rick Barrette' 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' 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.4.0' version '0.4.2'
url 'https://github.com/rickbarrette/redmine_qbo' url 'https://github.com/rickbarrette/redmine_qbo'
author_url 'http://rickbarrette.org' author_url 'http://rickbarrette.org'
settings :default => {'empty' => true}, :partial => 'qbo/settings' settings :default => {'empty' => true}, :partial => 'qbo/settings'
@@ -50,17 +48,20 @@ Redmine::Plugin.register :redmine_qbo do
# set per_page globally # set per_page globally
WillPaginate.per_page = 10 WillPaginate.per_page = 10
permission :view_customers, :customers => :index, :public => false
permission :add_customers, :customers => :new, :public => false
permission :view_payments, :payments => :index, :public => false
permission :add_payments, :payments => :new, :public => false
permission :view_vehicles, :payments => :new, :public => false
# Register QBO top menu item # Register QBO top menu item
#menu :top_menu, :qbo, { :controller => :qbo, :action => :index }, :caption => 'Quickbooks', :if => Proc.new { User.current.admin? } #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, :customers, { :controller => :customers, :action => :index }, :caption => 'Customers', :if => Proc.new {User.current.logged?}
menu :top_menu, :vehicles, { :controller => :vehicles, :action => :index }, :caption => 'Vehicles', :if => Proc.new { User.current.logged? } menu :top_menu, :vehicles, { :controller => :vehicles, :action => :index }, :caption => 'Vehicles', :if => Proc.new { User.current.allowed_to?(:view_vehicles, @project) }
menu :application_menu, :new_customer, { :controller => :customers, :action => :new }, :caption => 'New Customer', :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.logged? } menu :application_menu, :new_payment, { :controller => :payments, :action => :new }, :caption => 'New Payment', :if => Proc.new { User.current.allowed_to?(:add_payments, @project)}
permission :customers, { :customers => [:index, :new] }, :public => false
menu :project_menu, :customers, { :controller => 'customers', :action => 'new' }, :caption => 'New Customer', :after => :new_issue, :param => :project_id menu :project_menu, :customers, { :controller => 'customers', :action => 'new' }, :caption => 'New Customer', :after => :new_issue, :param => :project_id
permission :payments, { :payments => [:index, :new] }, :public => false
menu :project_menu, :payments, { :controller => 'payments', :action => 'new' }, :caption => 'New Payment', :after => :customers, :param => :project_id menu :project_menu, :payments, { :controller => 'payments', :action => 'new' }, :caption => 'New Payment', :after => :customers, :param => :project_id
end end
+38
View File
@@ -0,0 +1,38 @@
#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 'attachments_controller'
module AttachmentsControllerPatch
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
skip_before_action :read_authorize
end
end
module ClassMethods
end
module InstanceMethods
end
end
# Add module to AttachmentsController
AttachmentsController.send(:include, AttachmentsControllerPatch)
+1 -1
View File
@@ -14,6 +14,6 @@ class HeaderFooterHookListener < Redmine::Hook::ViewListener
end end
def view_layouts_base_body_bottom(context = {}) def view_layouts_base_body_bottom(context = {})
return "<div class='footer' align='center'><b>Last Sync: </b> #{Qbo.last_sync}</div>" return "<div id='qbo_footer' align='center'><b>Last Sync: </b> #{Qbo.last_sync if Qbo.exists?}</div>"
end end
end end
+7 -1
View File
@@ -23,6 +23,7 @@ module IssuePatch
base.class_eval do base.class_eval do
unloadable # Send unloadable so it will not be unloaded in development unloadable # Send unloadable so it will not be unloaded in development
belongs_to :customer, primary_key: :id belongs_to :customer, primary_key: :id
belongs_to :customer_token, primary_key: :id
belongs_to :qbo_estimate, primary_key: :id belongs_to :qbo_estimate, primary_key: :id
has_and_belongs_to_many :qbo_invoices has_and_belongs_to_many :qbo_invoices
#, :association_foreign_key => 'issue_id', :class_name => 'Issue', :join_table => 'issues_qbo_invoices' #, :association_foreign_key => 'issue_id', :class_name => 'Issue', :join_table => 'issues_qbo_invoices'
@@ -71,7 +72,7 @@ module IssuePatch
item = item_service.query("SELECT * FROM Item WHERE Name = '#{key}' ").first item = item_service.query("SELECT * FROM Item WHERE Name = '#{key}' ").first
next if item.nil? next if item.nil?
time_entry.description = "#{tracker} ##{id}: #{subject} #{"(Partial)" if not closed?}" time_entry.description = "#{tracker} ##{id}: #{subject} #{"(Partial @ #{done_ratio}%)" if not closed?}"
# TODO entry.user.qbo_employee.id # TODO entry.user.qbo_employee.id
time_entry.employee_id = assigned_to.qbo_employee_id time_entry.employee_id = assigned_to.qbo_employee_id
time_entry.customer_id = customer_id time_entry.customer_id = customer_id
@@ -90,6 +91,11 @@ module IssuePatch
end end
end end
# Create a shareable link for a customer
def share_token
CustomerToken.create(:expires_at => Time.now + 1.month, :issue_id => id)
end
end end
# Add module to Issue # Add module to Issue
+8 -6
View File
@@ -12,8 +12,9 @@ class IssuesFormHookListener < Redmine::Hook::ViewListener
# Load the javascript # Load the javascript
def view_layouts_base_html_head(context = {}) def view_layouts_base_html_head(context = {})
javascript_include_tag 'vehicles', :plugin => 'redmine_qbo' js = javascript_include_tag 'application', :plugin => 'redmine_qbo'
javascript_include_tag 'autocomplete', :plugin => 'redmine_qbo' js += javascript_include_tag 'autocomplete-rails', :plugin => 'redmine_qbo'
return js
end end
# Edit Issue Form # Edit Issue Form
@@ -26,9 +27,10 @@ class IssuesFormHookListener < Redmine::Hook::ViewListener
selected_estimate = context[:issue].qbo_estimate ? context[:issue].qbo_estimate.id : nil selected_estimate = context[:issue].qbo_estimate ? context[:issue].qbo_estimate.id : nil
selected_vehicle = context[:issue].vehicles_id ? context[:issue].vehicles_id : nil selected_vehicle = context[:issue].vehicles_id ? context[:issue].vehicles_id : nil
# Load customer information without callbacks # Load customer information
customer = Customer.find_by_id(selected_customer) if selected_customer customer = Customer.find_by_id(selected_customer) if selected_customer
select_customer = f.select :customer_id, Customer.all.pluck(:name, :id).sort, :selected => selected_customer, include_blank: true search_customer = f.autocomplete_field :customer, autocomplete_customer_name_customers_path, :selected => selected_customer, :update_elements => {:id => '#issue_customer_id', :value => '#issue_customer'}
customer_id = f.hidden_field :customer_id, :id => "issue_customer_id"
# Generate the drop down list of quickbooks extimates # Generate the drop down list of quickbooks extimates
select_estimate = f.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
@@ -36,11 +38,11 @@ class IssuesFormHookListener < Redmine::Hook::ViewListener
if context[:issue].customer if context[:issue].customer
vehicles = customer.vehicles.pluck(:name, :id).sort! vehicles = customer.vehicles.pluck(:name, :id).sort!
else else
vehicles = Vehicle.all.order(:name).pluck(:name, :id) vehicles = [nil].compact
end end
vehicle = f.select :vehicles_id, vehicles, :selected => selected_vehicle, include_blank: true vehicle = f.select :vehicles_id, vehicles, :selected => selected_vehicle, include_blank: true
return "<p>#{select_customer}</p> <p>#{select_estimate}</p> <p>#{vehicle}</p>" return "<p><label for=\"issue_customer\">Customer</label>#{search_customer} #{customer_id}</p> <p>#{select_estimate}</p> <p>#{vehicle}</p>"
end end
end end
+9 -5
View File
@@ -52,8 +52,9 @@ class IssuesShowHookListener < Redmine::Hook::ViewListener
split_vin = vin.scan(/.{1,9}/) if vin split_vin = vin.scan(/.{1,9}/) if vin
return " return "
<div class=\"attributes\"> <div class=\"splitcontent\">
<div class=\"splitcontentleft\">
<div class=\"customer_id attribute\"> <div class=\"customer_id attribute\">
<div class=\"label\"><span>Customer</span>:</div> <div class=\"label\"><span>Customer</span>:</div>
<div class=\"value\">#{customer}</div> <div class=\"value\">#{customer}</div>
@@ -68,9 +69,9 @@ class IssuesShowHookListener < Redmine::Hook::ViewListener
<div class=\"label\"><span>Invoice</span>:</div> <div class=\"label\"><span>Invoice</span>:</div>
<div class=\"value\">#{invoice_link}</div> <div class=\"value\">#{invoice_link}</div>
</div> </div>
</div>
<br/> <div class=\"splitcontentleft\">
<div class=\"vehicle attribute\"> <div class=\"vehicle attribute\">
<div class=\"label\"><span>Vehicle</span>:</div> <div class=\"label\"><span>Vehicle</span>:</div>
<div class=\"value\">#{vehicle}</div> <div class=\"value\">#{vehicle}</div>
@@ -85,11 +86,14 @@ class IssuesShowHookListener < Redmine::Hook::ViewListener
<div class=\"label\"><span>Notes</span>:</div> <div class=\"label\"><span>Notes</span>:</div>
<div class=\"value\">#{notes}</div> <div class=\"value\">#{notes}</div>
</div> </div>
</div> " </div>
</div>"
end end
def view_issues_show_description_bottom(context={}) def view_issues_show_description_bottom(context={})
return "<br/> #{button_to "Bill Time", "#{Redmine::Utils::relative_url_root}/qbo/bill/#{context[:issue].id}", method: :get}" 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?
return "<br/> #{bill_button} #{share_button}"
end end
end end
+1 -1
View File
@@ -51,7 +51,7 @@ module IssuesPdfHelperPatch
vin = v ? v.vin : nil vin = v ? v.vin : nil
notes = v ? v.notes : nil notes = v ? v.notes : nil
left << [l(:field_vehicles), vehicle] left << [l(:field_vehicles), vehicle]
left << [l(:field_vin), vin.gsub(/(.{9})/, '\1 ')] left << [l(:field_vin), vin ? vin.gsub(/(.{9})/, '\1 ') : nil]
#left << [l(:field_notes), notes] #left << [l(:field_notes), notes]
right = [] right = []
+1
View File
@@ -36,6 +36,7 @@ module QueryPatch
unless @available_columns unless @available_columns
@available_columns = available_columns_without_qbo @available_columns = available_columns_without_qbo
@available_columns << QueryColumn.new(:customer, :sortable => "#{Customer.table_name}.name", :groupable => true, :caption => :field_customer) @available_columns << QueryColumn.new(:customer, :sortable => "#{Customer.table_name}.name", :groupable => true, :caption => :field_customer)
@available_columns << QueryColumn.new(:qbo_billed, :sortable => "#{TimeEntry.table_name}.qbo_billed", :groupable => true, :caption => :field_qbo_billed)
end end
@available_columns @available_columns
end end
+71
View File
@@ -0,0 +1,71 @@
#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 'time_entry_query'
module TimeEntryQueryPatch
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_billed
alias_method_chain :available_filters, :qbo_billed
end
end
module ClassMethods
end
module InstanceMethods
def available_columns_with_qbo_billed
unless @available_columns
@available_columns = available_columns_without_qbo
@available_columns << QueryColumn.new(:qbo_billed, :sortable => "#{TimeEntry.table_name}.name", :groupable => true, :caption => :field_qbo_billed)
end
@available_columns
end
def available_filters_with_qbo_billed
unless @available_filters
@available_filters = available_filters_without_qbo
#qbo_filters = {
# :customer => {
# :id => l(:field_qbo_billed),
# :type => :boolean,
# :order => @available_filters.size + 1},
#}
qbo_filters = {
"qbo_billed" => {
:id => :qbo_billed,
: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)
end
@available_filters
end
end
end
# Add module to TimeEntryQuery
TimeEntryQuery.send(:include, QueryPatch)
+9
View File
@@ -0,0 +1,9 @@
require File.expand_path('../../test_helper', __FILE__)
class CustomerTokenTest < ActiveSupport::TestCase
# Replace this with your real tests.
def test_truth
assert true
end
end