mirror of
https://github.com/rickbarrette/redmine_qbo.git
synced 2026-04-02 16:21:58 -04:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 659a1fbcf0 | |||
| 4dc1f5d0bd | |||
| 02f34582f4 | |||
| 2f9ef6304f | |||
| 886d5f4ace | |||
| 1ade938eb3 | |||
| 3111f391f3 |
2
init.rb
2
init.rb
@@ -14,7 +14,7 @@ Redmine::Plugin.register :redmine_qbo do
|
|||||||
name 'Redmine QBO plugin'
|
name 'Redmine QBO plugin'
|
||||||
author 'Rick Barrette'
|
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'
|
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.2.8'
|
version '2026.2.11'
|
||||||
url 'https://github.com/rickbarrette/redmine_qbo'
|
url 'https://github.com/rickbarrette/redmine_qbo'
|
||||||
author_url 'https://barrettefabrication.com'
|
author_url 'https://barrettefabrication.com'
|
||||||
settings default: {empty: true}, partial: 'qbo/settings'
|
settings default: {empty: true}, partial: 'qbo/settings'
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ module RedmineQbo
|
|||||||
|
|
||||||
# Same as typing in the class
|
# Same as typing in the class
|
||||||
base.class_eval do
|
base.class_eval do
|
||||||
belongs_to :customer, primary_key: :id
|
belongs_to :customer, class_name: 'Customer', foreign_key: :customer_id, optional: true
|
||||||
belongs_to :customer_token, primary_key: :id
|
belongs_to :customer_token, primary_key: :id
|
||||||
belongs_to :estimate, primary_key: :id
|
belongs_to :estimate, primary_key: :id
|
||||||
has_and_belongs_to_many :invoices
|
has_and_belongs_to_many :invoices
|
||||||
@@ -43,15 +43,19 @@ module RedmineQbo
|
|||||||
|
|
||||||
# Create billable time entries
|
# Create billable time entries
|
||||||
def bill_time
|
def bill_time
|
||||||
logger.debug "QBO: Billing time for issue ##{id}"
|
logger.debug "QBO: Billing time for issue ##{self.id}"
|
||||||
return false if assigned_to.nil?
|
logger.debug "Issue is closed? #{self.closed?}"
|
||||||
|
|
||||||
|
return false if self.assigned_to.nil?
|
||||||
return false unless Qbo.first
|
return false unless Qbo.first
|
||||||
return false unless customer
|
return false unless self.customer
|
||||||
|
|
||||||
Thread.new do
|
Thread.new do
|
||||||
spent_time = time_entries.where(billed: [false, nil])
|
spent_time = self.time_entries.where(billed: [false, nil])
|
||||||
spent_hours ||= spent_time.sum(:hours) || 0
|
spent_hours ||= spent_time.sum(:hours) || 0
|
||||||
|
|
||||||
|
logger.debug "Issue has spent hours: #{spent_hours}"
|
||||||
|
|
||||||
if spent_hours > 0 then
|
if spent_hours > 0 then
|
||||||
|
|
||||||
# Prepare to create a new Time Activity
|
# Prepare to create a new Time Activity
|
||||||
@@ -73,20 +77,23 @@ module RedmineQbo
|
|||||||
|
|
||||||
# Now letes upload our totals for each activity as their own billable time entry
|
# Now letes upload our totals for each activity as their own billable time entry
|
||||||
h.each do |key, val|
|
h.each do |key, val|
|
||||||
|
logger.debug "Processing activity '#{key}' with #{val.to_i} hours for issue ##{self.id}"
|
||||||
|
|
||||||
# Convert float spent time to hours and minutes
|
# Convert float spent time to hours and minutes
|
||||||
hours = val.to_i
|
hours = val.to_i
|
||||||
minutesDecimal = (( val - hours) * 60)
|
minutesDecimal = (( val - hours) * 60)
|
||||||
minutes = minutesDecimal.to_i
|
minutes = minutesDecimal.to_i
|
||||||
|
|
||||||
|
logger.debug "Converted #{val.to_i} hours to #{hours} hours and #{minutes} minutes"
|
||||||
|
|
||||||
# Lets match the activity to an qbo item
|
# Lets match the activity to an qbo item
|
||||||
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?
|
||||||
|
|
||||||
# Create the new billable time entry and upload it
|
# Create the new billable time entry and upload it
|
||||||
time_entry.description = "#{tracker} ##{id}: #{subject} #{"(Partial @ #{done_ratio}%)" if not closed?}"
|
time_entry.description = "#{self.tracker} ##{self.id}: #{self.subject} #{"(Partial @ #{self.done_ratio}%)" unless self.closed?}"
|
||||||
time_entry.employee_id = assigned_to.employee_id
|
time_entry.employee_id = self.assigned_to.employee_id
|
||||||
time_entry.customer_id = customer_id
|
time_entry.customer_id = self.customer_id
|
||||||
time_entry.billable_status = "Billable"
|
time_entry.billable_status = "Billable"
|
||||||
time_entry.hours = hours
|
time_entry.hours = hours
|
||||||
time_entry.minutes = minutes
|
time_entry.minutes = minutes
|
||||||
|
|||||||
@@ -14,6 +14,20 @@ module RedmineQbo
|
|||||||
module Patches
|
module Patches
|
||||||
module QueryPatch
|
module QueryPatch
|
||||||
|
|
||||||
|
def base_scope
|
||||||
|
scope = super
|
||||||
|
|
||||||
|
if filters['customer_name'].present?
|
||||||
|
scope = scope.left_outer_joins(:customer)
|
||||||
|
end
|
||||||
|
|
||||||
|
if has_column?(:customer) || filters['customer_name'].present?
|
||||||
|
scope = scope.includes(:customer)
|
||||||
|
end
|
||||||
|
|
||||||
|
scope
|
||||||
|
end
|
||||||
|
|
||||||
# Add qbo options to the aviable columns
|
# Add qbo options to the aviable columns
|
||||||
def available_columns
|
def available_columns
|
||||||
unless @available_columns
|
unless @available_columns
|
||||||
@@ -26,10 +40,27 @@ module RedmineQbo
|
|||||||
|
|
||||||
# Add customers to filters
|
# Add customers to filters
|
||||||
def initialize_available_filters
|
def initialize_available_filters
|
||||||
#add_available_filter "customer", type: :text
|
#add_available_filter "customer_id", type: :list, name: l(:field_customer), :values => lambda {Customer.pluck(:name, :id).map {|name, id| [name, id.to_s]}}
|
||||||
|
add_available_filter( 'customer_name', type: :text, name: l(:field_customer))
|
||||||
super
|
super
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def sql_for_customer_name_field(field, operator, value)
|
||||||
|
pattern = "%#{value.first}%"
|
||||||
|
|
||||||
|
sql = case operator
|
||||||
|
when '~'
|
||||||
|
"#{Customer.table_name}.name LIKE ?"
|
||||||
|
when '!~'
|
||||||
|
"#{Customer.table_name}.name NOT LIKE ?"
|
||||||
|
else
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
Issue.joins(:customer).sanitize_sql_for_conditions([sql, pattern])
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add module to Issue
|
# Add module to Issue
|
||||||
|
|||||||
Reference in New Issue
Block a user