mirror of
https://github.com/rickbarrette/redmine_qbo.git
synced 2026-04-02 16:21:58 -04:00
Compare commits
8 Commits
1a10360884
...
2026.3.9
| Author | SHA1 | Date | |
|---|---|---|---|
| db3c6021c5 | |||
| b8327be5d6 | |||
| c4e1ece82c | |||
| 9fd7140e4a | |||
| a6c8923ea9 | |||
| eb1174cf7c | |||
| 7993f15441 | |||
| bb57af71ae |
@@ -46,9 +46,13 @@ class QboController < ApplicationController
|
||||
redirect_to issue || root_path, flash: { error: e.message }
|
||||
end
|
||||
|
||||
# Manual sync endpoint to trigger a full synchronization of QuickBooks entities with the local database. Enqueues all relevant sync jobs and redirects to the home page with a notice that syncing has started.
|
||||
# Manual sync endpoint to trigger synchronization of QuickBooks entities
|
||||
# with the local database. Supports full or partial sync depending on
|
||||
# the `full_sync` boolean parameter.
|
||||
def sync
|
||||
QboSyncDispatcher.full_sync!
|
||||
full_sync = ActiveModel::Type::Boolean.new.cast(params[:full_sync])
|
||||
QboSyncDispatcher.sync!(full_sync: full_sync)
|
||||
|
||||
redirect_to :home, flash: { notice: I18n.t(:label_syncing) }
|
||||
end
|
||||
|
||||
|
||||
@@ -17,9 +17,17 @@ class QboSyncDispatcher
|
||||
Employee
|
||||
].freeze
|
||||
|
||||
# Dispatches all synchronization jobs to perform a full sync of QuickBooks entities with the local database. Each job is enqueued with the `full_sync` flag set to true.
|
||||
def self.full_sync!
|
||||
# Dispatches all synchronization jobs to perform a full sync of QuickBooks entities with the local database.
|
||||
# Each job is enqueued with the `full_sync` flag set to true.
|
||||
def self.sync!(full_sync: false)
|
||||
log "Manual Sync initated for #{full_sync ? "full sync" : "incremental sync"}"
|
||||
enque_jobs full_sync: full_sync
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Dynamically enques all sync jobs
|
||||
def self.enque_jobs(full_sync: full_sync)
|
||||
jobs = SYNC_JOBS.dup
|
||||
|
||||
# Allow other plugins to add addtional sync jobs via Hooks
|
||||
@@ -29,11 +37,9 @@ class QboSyncDispatcher
|
||||
log "Added additionals QBO Sync Job for #{context.to_s}"
|
||||
end
|
||||
|
||||
jobs.each { |job| QboSyncJob.perform_later(entity: job, full_sync: true) }
|
||||
jobs.each { |job| QboSyncJob.perform_later(entity: job, full_sync: full_sync) }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.log(msg)
|
||||
Rails.logger.info "[QboSyncDispatcher] #{msg}"
|
||||
end
|
||||
|
||||
@@ -33,14 +33,12 @@ class Customer < QboBaseModel
|
||||
|
||||
# Returns the customer's email address
|
||||
def email
|
||||
details
|
||||
return @details&.email_address&.address
|
||||
return details&.email_address&.address
|
||||
end
|
||||
|
||||
# Updates the customer's email address
|
||||
def email=(s)
|
||||
details
|
||||
@details.email_address = s
|
||||
details.email_address = s
|
||||
end
|
||||
|
||||
# Customers are not bound by a project
|
||||
@@ -51,22 +49,19 @@ class Customer < QboBaseModel
|
||||
|
||||
# returns the customer's mobile phone
|
||||
def mobile_phone
|
||||
details
|
||||
return @details&.mobile_phone&.free_form_number
|
||||
return details&.mobile_phone&.free_form_number
|
||||
end
|
||||
|
||||
# Updates the custome's mobile phone number
|
||||
def mobile_phone=(n)
|
||||
details
|
||||
pn = Quickbooks::Model::TelephoneNumber.new
|
||||
pn.free_form_number = n
|
||||
@details.mobile_phone = pn
|
||||
details.mobile_phone = pn
|
||||
end
|
||||
|
||||
# Updates Both local DB name & QBO display_name
|
||||
def name=(s)
|
||||
details
|
||||
@details.display_name = s
|
||||
details.display_name = s
|
||||
super
|
||||
end
|
||||
|
||||
@@ -78,22 +73,19 @@ class Customer < QboBaseModel
|
||||
|
||||
# Sets the notes for the customer
|
||||
def notes=(s)
|
||||
details
|
||||
@details.notes = s
|
||||
details.notes = s
|
||||
end
|
||||
|
||||
# returns the customer's primary phone
|
||||
def primary_phone
|
||||
details
|
||||
return @details&.primary_phone&.free_form_number
|
||||
return details&.primary_phone&.free_form_number
|
||||
end
|
||||
|
||||
# Updates the customer's primary phone number
|
||||
def primary_phone=(n)
|
||||
details
|
||||
pn = Quickbooks::Model::TelephoneNumber.new
|
||||
pn.free_form_number = n
|
||||
@details.primary_phone = pn
|
||||
details.primary_phone = pn
|
||||
end
|
||||
|
||||
# Seach for customers by name or phone number
|
||||
|
||||
@@ -14,6 +14,26 @@ class Qbo < ActiveRecord::Base
|
||||
include Redmine::I18n
|
||||
|
||||
validate :single_record_only, on: :create
|
||||
|
||||
# Returns the last sync time formatted for display. If no sync has occurred, returns a default message.
|
||||
def self.last_sync
|
||||
qbo = QboConnectionService.current!
|
||||
format_time(qbo.last_sync)
|
||||
rescue
|
||||
return I18n.t(:label_qbo_never_synced)
|
||||
end
|
||||
|
||||
def self.oauth2_access_token_expires_at
|
||||
QboConnectionService.current!.oauth2_access_token_expires_at
|
||||
rescue
|
||||
return I18n.t(:label_qbo_never_synced)
|
||||
end
|
||||
|
||||
def self.oauth2_refresh_token_expires_at
|
||||
QboConnectionService.current!.oauth2_refresh_token_expires_at
|
||||
rescue
|
||||
return I18n.t(:label_qbo_never_synced)
|
||||
end
|
||||
|
||||
# Updates last sync time stamp
|
||||
def self.update_time_stamp
|
||||
@@ -24,13 +44,6 @@ class Qbo < ActiveRecord::Base
|
||||
qbo.save
|
||||
end
|
||||
|
||||
# Returns the last sync time formatted for display. If no sync has occurred, returns a default message.
|
||||
def self.last_sync
|
||||
qbo = QboConnectionService.current!
|
||||
return I18n.t(:label_qbo_never_synced) unless qbo&.last_sync
|
||||
format_time(qbo.last_sync)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Logs a message with a QBO-specific prefix for easier identification in the logs.
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
|
||||
class QboOauthService
|
||||
|
||||
# Generates the QuickBooks OAuth authorization URL with the specified callback URL. The URL includes necessary parameters such as response type, state, and scope.
|
||||
# Generates the QuickBooks OAuth authorization URL with the specified callback URL.
|
||||
# The URL includes necessary parameters such as response type, state, and scope.
|
||||
def self.authorization_url(callback_url:)
|
||||
client.auth_code.authorize_url(
|
||||
redirect_uri: callback_url,
|
||||
@@ -20,7 +21,8 @@ class QboOauthService
|
||||
)
|
||||
end
|
||||
|
||||
# Exchanges the authorization code for access and refresh tokens. Creates or replaces the QBO connection record with the new credentials and refreshes the token immediately after creation.
|
||||
# Exchanges the authorization code for access and refresh tokens.
|
||||
# Creates or replaces the QBO connection record with the new credentials and refreshes the token immediately after creation.
|
||||
def self.exchange!(code:, callback_url:, realm_id:)
|
||||
resp = client.auth_code.get_token(code, redirect_uri: callback_url)
|
||||
QboConnectionService.replace!( token: resp.token, refresh_token: resp.refresh_token, realm_id: realm_id )
|
||||
|
||||
@@ -3,3 +3,4 @@
|
||||
<%= submit_tag t(:label_search) %>
|
||||
<% end %>
|
||||
<%= button_to t(:label_new_customer), new_customer_path, method: :get%>
|
||||
<%= button_to(t(:label_sync), qbo_sync_path, method: :get) if User.current.admin?%>
|
||||
|
||||
@@ -66,12 +66,12 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
||||
|
||||
<tr>
|
||||
<th><%=t(:label_oauth_expires)%></th>
|
||||
<td><%= QboConnectionService.current!&.oauth2_access_token_expires_at %>
|
||||
<td><%= Qbo.oauth2_access_token_expires_at %>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th><%=t(:label_oauth2_refresh_token_expires_at)%></th>
|
||||
<td><%= QboConnectionService.current!&.oauth2_refresh_token_expires_at %>
|
||||
<td><%= Qbo.oauth2_refresh_token_expires_at %>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
@@ -107,5 +107,5 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
|
||||
<br/>
|
||||
|
||||
<div>
|
||||
<b><%=t(:label_last_sync)%> </b> <%= Qbo.last_sync if Qbo.exists? %> <%= link_to t(:label_sync_now), qbo_sync_path %>
|
||||
<b><%=t(:label_last_sync)%> </b> <%= Qbo.last_sync if Qbo.exists? %> <%= link_to t(:label_sync_now), qbo_sync_path(full_sync: true) %>
|
||||
</div>
|
||||
|
||||
2
init.rb
2
init.rb
@@ -14,7 +14,7 @@ Redmine::Plugin.register :redmine_qbo do
|
||||
name 'Redmine QBO plugin'
|
||||
author 'Rick Barrette'
|
||||
description 'A pluging for Redmine to connect with QuickBooks Online to create Time Activity Entries for billable hours logged when an Issue is closed'
|
||||
version '2026.3.7'
|
||||
version '2026.3.9'
|
||||
url 'https://github.com/rickbarrette/redmine_qbo'
|
||||
author_url 'https://barrettefabrication.com'
|
||||
settings default: {empty: true}, partial: 'qbo/settings'
|
||||
|
||||
Reference in New Issue
Block a user