Merge branch 'redmine-5'

This commit is contained in:
2023-12-30 23:35:25 -05:00
8 changed files with 157 additions and 126 deletions

View File

@@ -1,6 +1,6 @@
#The MIT License (MIT) #The MIT License (MIT)
# #
#Copyright (c) 2022 rick barrette #Copyright (c) 2023 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: #Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
# #
@@ -20,10 +20,13 @@ class InvoiceController < ApplicationController
# #
def show def show
begin begin
base = Invoice.get_base qbo = Qbo.first
invoice = base.fetch_by_id(params[:id]) qbo.perform_authenticated_request do |access_token|
@pdf = base.pdf(invoice) service = Quickbooks::Service::Invoice.new(:company_id => qbo.realm_id, :access_token => access_token)
send_data @pdf, filename: "invoice #{invoice.doc_number}.pdf", :disposition => 'inline', :type => "application/pdf" invoice = service.fetch_by_id(params[:id])
@pdf = service.pdf(invoice)
send_data @pdf, filename: "invoice #{invoice.doc_number}.pdf", :disposition => 'inline', :type => "application/pdf"
end
rescue rescue
redirect_to :back, :flash => { :error => "Invoice not found" } redirect_to :back, :flash => { :error => "Invoice not found" }
end end

View File

@@ -1,6 +1,6 @@
#The MIT License (MIT) #The MIT License (MIT)
# #
#Copyright (c) 2022 rick barrette #Copyright (c) 2023 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: #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:
# #
@@ -142,17 +142,14 @@ class Customer < ActiveRecord::Base
# proforms a bruteforce sync operation # proforms a bruteforce sync operation
# This needs to be simplified # This needs to be simplified
def self.sync def self.sync
service = Qbo.get_base(:customer)
# Sync ALL customers if the database is empty # Sync ALL customers if the database is empty
#if count == 0 qbo = Qbo.first
customers = service.all customers = qbo.perform_authenticated_request do |access_token|
#else service = Quickbooks::Service::Customer.new(:company_id => qbo.realm_id, :access_token => access_token)
# last = Qbo.first.last_sync service.all
# query = "Select Id, DisplayName From Customer" end
# query << " Where Metadata.LastUpdatedTime >= '#{last.iso8601}' " if last
# customers = service.query(query) return unless customers
#end
customers.each do |c| customers.each do |c|
logger.info "Processing customer #{c.id}" logger.info "Processing customer #{c.id}"
@@ -180,9 +177,14 @@ class Customer < ActiveRecord::Base
# proforms a bruteforce sync operation # proforms a bruteforce sync operation
# This needs to be simplified # This needs to be simplified
def self.sync_by_id(id) def self.sync_by_id(id)
service = Qbo.get_base(:customer) qbo = Qbo.first
customer = qbo.perform_authenticated_request do |access_token|
service = Quickbooks::Service::Customer.new(:company_id => qbo.realm_id, :access_token => access_token)
service.fetch_by_id(id)
end
return unless customer
customer = service.fetch_by_id(id)
customer = Customer.find_or_create_by(id: customer.id) customer = Customer.find_or_create_by(id: customer.id)
if customer.active? if customer.active?
if not customer.name.eql? customer.display_name if not customer.name.eql? customer.display_name
@@ -200,7 +202,11 @@ class Customer < ActiveRecord::Base
# Push the updates # Push the updates
def save_with_push def save_with_push
begin begin
@details = Qbo.get_base(:customer).update(@details) qbo = Qbo.first
@details = qbo.perform_authenticated_request do |access_token|
service = Quickbooks::Service::Customer.new(:company_id => qbo.realm_id, :access_token => access_token)
serivce.update(@details)
end
#raise "QBO Fault" if @details.fault? #raise "QBO Fault" if @details.fault?
self.id = @details.id self.id = @details.id
rescue Exception => e rescue Exception => e
@@ -218,7 +224,11 @@ class Customer < ActiveRecord::Base
def pull def pull
begin begin
raise Exception unless self.id raise Exception unless self.id
@details = Qbo.get_base(:customer).fetch_by_id(self.id) qbo = Qbo.first
@details = qbo.perform_authenticated_request do |access_token|
service = Quickbooks::Service::Customer.new(:company_id => qbo.realm_id, :access_token => access_token)
service.fetch_by_id(self.id)
end
rescue Exception => e rescue Exception => e
@details = Quickbooks::Model::Customer.new @details = Quickbooks::Model::Customer.new
end end

View File

@@ -13,12 +13,14 @@ class Employee < ActiveRecord::Base
has_many :users has_many :users
validates_presence_of :id, :name validates_presence_of :id, :name
def self.get_base
Qbo.get_base(:employee)
end
def self.sync def self.sync
employees = get_base.all qbo = Qbo.first
employees = qbo.perform_authenticated_request do |access_token|
service = Quickbooks::Service::Employee.new(:company_id => qbo.realm_id, :access_token => access_token)
service.all
end
return unless employees
transaction do transaction do
# Update the item table # Update the item table
@@ -33,7 +35,13 @@ class Employee < ActiveRecord::Base
end end
def self.sync_by_id(id) def self.sync_by_id(id)
employee = get_base.fetch_by_id(id) qbo = Qbo.first
employee = qbo.perform_authenticated_request do |access_token|
service = Quickbooks::Service::Employee.new(:company_id => qbo.realm_id, :access_token => access_token)
service.fetch_by_id(id)
end
return unless employee
employee = find_or_create_by(id: employee.id) employee = find_or_create_by(id: employee.id)
employee.name = employee.display_name employee.name = employee.display_name
employee.id = employee.id employee.id = employee.id

View File

@@ -1,6 +1,6 @@
#The MIT License (MIT) #The MIT License (MIT)
# #
#Copyright (c) 2022 rick barrette #Copyright (c) 2023 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: #Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
# #
@@ -16,15 +16,17 @@ class Estimate < ActiveRecord::Base
validates_presence_of :doc_number, :id validates_presence_of :doc_number, :id
self.primary_key = :id self.primary_key = :id
# return the QBO Estimate service
def self.get_base
Qbo.get_base(:estimate)
end
# sync all estimates # sync all estimates
def self.sync def self.sync
logger.debug "Syncing ALL estimates" logger.debug "Syncing ALL estimates"
estimates = get_base.all qbo = Qbo.first
estimates = qbo.perform_authenticated_request do |access_token|
service = Quickbooks::Service::Estimate.new(:company_id => qbo.realm_id, :access_token => access_token)
service.all
end
return unless estimates
estimates.each { |estimate| estimates.each { |estimate|
process_estimate(estimate) process_estimate(estimate)
} }
@@ -36,17 +38,28 @@ class Estimate < ActiveRecord::Base
# sync only one estimate # sync only one estimate
def self.sync_by_id(id) def self.sync_by_id(id)
logger.debug "Syncing estimate #{id}" logger.debug "Syncing estimate #{id}"
process_estimate(get_base.fetch_by_id(id)) qbo = Qbo.first
qbo.perform_authenticated_request do |access_token|
service = Quickbooks::Service::Estimate.new(:company_id => qbo.realm_id, :access_token => access_token)
process_estimate(service.fetch_by_id(id))
end
end end
# update an estimate # update an estimate
def self.update(id) def self.update(id)
# Update the item table # Update the item table
estimate = get_base.fetch_by_id(id) qbo = Qbo.first
estimate = find_or_create_by(id: id) estimate = qbo.perform_authenticated_request do |access_token|
estimate.doc_number = estimate.doc_number service = Quickbooks::Service::Estimate.new(:company_id => qbo.realm_id, :access_token => access_token)
estimate.txn_date = estimate.txn_date service.fetch_by_id(id)
estimate.save! end
return unless estimate
e = find_or_create_by(id: id)
e.doc_number = estimate.doc_number
e.txn_date = estimate.txn_date
e.save!
end end
# process an estimate into the database # process an estimate into the database
@@ -62,9 +75,12 @@ class Estimate < ActiveRecord::Base
# download the pdf from quickbooks # download the pdf from quickbooks
def pdf def pdf
base = Estimate.get_base qbo = Qbo.first
estimate = base.fetch_by_id(id) qbo.perform_authenticated_request do |access_token|
return base.pdf(estimate) service = Quickbooks::Service::Estimate.new(:company_id => qbo.realm_id, :access_token => access_token)
estimate = service.fetch_by_id(id)
service.pdf(estimate)
end
end end
# Magic Method # Magic Method
@@ -91,7 +107,11 @@ class Estimate < ActiveRecord::Base
def pull def pull
begin begin
raise Exception unless self.id raise Exception unless self.id
@details = Qbo.get_base(:estimate).fetch_by_id(self.id) qbo = Qbo.first
@details = qbo.perform_authenticated_request do |access_token|
service = Quickbooks::Service::Estimate.new(:company_id => qbo.realm_id, :access_token => access_token)
service(:estimate).fetch_by_id(self.id)
end
rescue Exception => e rescue Exception => e
@details = Quickbooks::Model::Estimate.new @details = Quickbooks::Model::Estimate.new
end end

View File

@@ -1,6 +1,6 @@
#The MIT License (MIT) #The MIT License (MIT)
# #
#Copyright (c) 2022 rick barrette #Copyright (c) 2023 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: #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:
# #
@@ -15,11 +15,6 @@ class Invoice < ActiveRecord::Base
validates_presence_of :doc_number, :id, :customer_id, :txn_date validates_presence_of :doc_number, :id, :customer_id, :txn_date
self.primary_key = :id self.primary_key = :id
# Get the quickbooks-ruby base for invoice
def self.get_base
Qbo.get_base(:invoice)
end
# sync ALL the invoices # sync ALL the invoices
def self.sync def self.sync
logger.debug "Syncing all invoices" logger.debug "Syncing all invoices"
@@ -30,12 +25,14 @@ class Invoice < ActiveRecord::Base
# TODO actually do something with the above query # TODO actually do something with the above query
# .all() is never called since count is never initialized # .all() is never called since count is never initialized
if count == 0 qbo = Qbo.first
invoices = get_base.all invoices = qbo.perform_authenticated_request do |access_token|
else service = Quickbooks::Service::Invoice.new(:company_id => qbo.realm_id, :access_token => access_token)
invoices = get_base.query() service.all
end end
return unless invoices
invoices.each { | invoice | invoices.each { | invoice |
process_invoice invoice process_invoice invoice
} }
@@ -44,8 +41,12 @@ class Invoice < ActiveRecord::Base
#sync by invoice ID #sync by invoice ID
def self.sync_by_id(id) def self.sync_by_id(id)
logger.debug "Syncing invoice #{id}" logger.debug "Syncing invoice #{id}"
invoice = get_base.fetch_by_id(id) qbo = Qbo.first
process_invoice invoice qbo.perform_authenticated_request do |access_token|
service = Quickbooks::Service::Invoice.new(:company_id => qbo.realm_id, :access_token => access_token)
invoice = service.fetch_by_id(id)
process_invoice invoice
end
end end
private private
@@ -155,7 +156,11 @@ class Invoice < ActiveRecord::Base
# Push updates # Push updates
begin begin
logger.debug "Trying to update invoice" logger.debug "Trying to update invoice"
get_base.update(invoice) if is_changed qbo = Qbo.first
qbo.perform_authenticated_request do |access_token|
service = Quickbooks::Service::Invoice.new(:company_id => qbo.realm_id, :access_token => access_token)
service.update(invoice) if is_changed
end
rescue rescue
# Do nothing, probaly custome field sync confict on the invoice. # Do nothing, probaly custome field sync confict on the invoice.
# This is a problem with how it's billed # This is a problem with how it's billed
@@ -187,7 +192,11 @@ class Invoice < ActiveRecord::Base
def pull def pull
begin begin
raise Exception unless self.id raise Exception unless self.id
@details = Qbo.get_base(:invoice).fetch_by_id(self.id) qbo = Qbo.first
@details = qbo.perform_authenticated_request do |access_token|
service = Quickbooks::Service::Invoice.new(:company_id => qbo.realm_id, :access_token => access_token)
service.fetch_by_id(self.id)
end
rescue Exception => e rescue Exception => e
@details = Quickbooks::Model::Invoice.new @details = Quickbooks::Model::Invoice.new
end end

View File

@@ -1,6 +1,6 @@
#The MIT License (MIT) #The MIT License (MIT)
# #
#Copyright (c) 2022 rick barrette #Copyright (c) 2023 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: #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:
# #
@@ -13,34 +13,6 @@ class Qbo < ActiveRecord::Base
include QuickbooksOauth include QuickbooksOauth
#
# Get a quickbooks base service object for type
# @params type of base
#
def self.get_base(type)
qbo = self.first
qbo.perform_authenticated_request do |access_token|
# build the reqiested service
case type
when :time_activity
return Quickbooks::Service::TimeActivity.new(:company_id => qbo.realm_id, :access_token => access_token)
when :customer
return Quickbooks::Service::Customer.new(:company_id => qbo.realm_id, :access_token => access_token)
when :invoice
return Quickbooks::Service::Invoice.new(:company_id => qbo.realm_id, :access_token => access_token)
when :estimate
return Quickbooks::Service::Estimate.new(:company_id => qbo.realm_id, :access_token => access_token)
when :employee
return Quickbooks::Service::Employee.new(:company_id => qbo.realm_id, :access_token => access_token)
when :item
return Quickbooks::Service::Item.new(:company_id => qbo.realm_id, :access_token => access_token)
else
return nil
end
end
end
# Updates last sync time stamp # Updates last sync time stamp
def self.update_time_stamp def self.update_time_stamp
date = DateTime.now date = DateTime.now

View File

@@ -1,6 +1,6 @@
#The MIT License (MIT) #The MIT License (MIT)
# #
#Copyright (c) 2022 rick barrette #Copyright (c) 2023 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: #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:
# #
@@ -26,7 +26,13 @@ class AddTxnDates < ActiveRecord::Migration[5.1]
say "Sync Invoices" say "Sync Invoices"
invoices = QboInvoice.get_base.all qbo = Qbo.first
invoices = qbo.perform_authenticated_request do |access_token|
service = Quickbooks::Service::Invoice.new(:company_id => qbo.realm_id, :access_token => access_token)
service.all
end
return unless invoices
invoices.each { |invoice| invoices.each { |invoice|
# Load the invoice into the database # Load the invoice into the database

View File

@@ -1,6 +1,6 @@
#The MIT License (MIT) #The MIT License (MIT)
# #
#Copyright (c) 2022 rick barrette #Copyright (c) 2023 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: #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:
# #
@@ -52,46 +52,49 @@ module IssuePatch
if spent_hours > 0 then if spent_hours > 0 then
# Prepare to create a new Time Activity # Prepare to create a new Time Activity
time_service = Qbo.get_base(:time_activity) qbo = Qbo.first
item_service = Qbo.get_base(:item) qbo.perform_authenticated_request do |access_token|
time_entry = Quickbooks::Model::TimeActivity.new time_service = Quickbooks::Service::TimeActivity.new(:company_id => qbo.realm_id, :access_token => access_token)
item_service = Quickbooks::Service::Item.new(:company_id => qbo.realm_id, :access_token => access_token)
time_entry = Quickbooks::Model::TimeActivity.new
# Lets total up each activity before billing. # Lets total up each activity before billing.
# This will simpify the invoicing with a single billable time entry per time activity # This will simpify the invoicing with a single billable time entry per time activity
h = Hash.new(0) h = Hash.new(0)
spent_time.each do |entry| spent_time.each do |entry|
h[entry.activity.name] += entry.hours h[entry.activity.name] += entry.hours
# update time entries billed status # update time entries billed status
entry.billed = true entry.billed = true
entry.save entry.save
end end
# 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|
# 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
# 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 = "#{tracker} ##{id}: #{subject} #{"(Partial @ #{done_ratio}%)" if not closed?}"
time_entry.employee_id = assigned_to.employee_id time_entry.employee_id = assigned_to.employee_id
time_entry.customer_id = customer_id time_entry.customer_id = 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
time_entry.name_of = "Employee" time_entry.name_of = "Employee"
time_entry.txn_date = Date.today time_entry.txn_date = Date.today
time_entry.hourly_rate = item.unit_price time_entry.hourly_rate = item.unit_price
time_entry.item_id = item.id time_entry.item_id = item.id
time_entry.start_time = start_date time_entry.start_time = start_date
time_entry.end_time = Time.now time_entry.end_time = Time.now
time_service.create(time_entry) time_service.create(time_entry)
end
end end
end end
end end