Class: CostAgent
- Inherits:
-
Object
- Object
- CostAgent
- Defined in:
- lib/costagent.rb
Overview
This exposes additional billable tracking functionality around the Freeagent API
Defined Under Namespace
Classes: Base, Invoice, InvoiceItem, Project, Task, Timeslip, User
Class Attribute Summary collapse
-
.cache_provider ⇒ Object
Returns the value of attribute cache_provider.
Instance Attribute Summary collapse
-
#password ⇒ Object
Our configuration for FA access.
-
#subdomain ⇒ Object
Our configuration for FA access.
-
#username ⇒ Object
Our configuration for FA access.
Class Method Summary collapse
-
.usd_rate ⇒ Object
This returns the current USD rate from xe.com (or falls back on 1.6 if there is an error).
Instance Method Summary collapse
-
#api(resource, parameters = {}) ⇒ Object
This calls the FA API for the specified resource.
-
#cache(resource, identifier, reload = false, &block) ⇒ Object
This calls out to the external third party provider for caching.
-
#client(resource, parameters = {}) ⇒ Object
This returns a client ready to query the FA API.
-
#earnt(start_date = DateTime.now, end_date = start_date) ⇒ Object
This returns the amount of GBP earnt in the specified timeframe.
-
#initialize(subdomain, username, password) ⇒ CostAgent
constructor
Initialize and validate input data.
-
#invoice(id) ⇒ Object
This returns the specific invoice by ID.
-
#invoices(reload = false) ⇒ Object
This returns all invoices.
-
#project(id) ⇒ Object
This returns the specified project.
-
#projects(filter = "active", reload = false) ⇒ Object
Returns all projects.
-
#tasks(project_id, reload = false) ⇒ Object
This returns all tasks for the specified project_id.
-
#timeslips(start_date = DateTime.now, end_date = start_date, reload = false) ⇒ Object
This returns all timeslips for the specified date range, with additional cost information.
-
#user(reload = false) ⇒ Object
This contains the logged in user information for the configured credentials.
-
#user_id ⇒ Object
This looks up the user ID using the CostAgent credentials.
-
#worked(start_date = DateTime.now, end_date = start_date) ⇒ Object
This returns the amount of hours worked.
Constructor Details
#initialize(subdomain, username, password) ⇒ CostAgent
Initialize and validate input data
56 57 58 59 60 61 62 63 64 |
# File 'lib/costagent.rb', line 56 def initialize(subdomain, username, password) self.subdomain = subdomain self.username = username self.password = password [:subdomain, :username, :password].each do |f| raise "No #{f} configured!" if self.send(f).nil? || self.send(f).empty? end end |
Class Attribute Details
.cache_provider ⇒ Object
Returns the value of attribute cache_provider.
39 40 41 |
# File 'lib/costagent.rb', line 39 def cache_provider @cache_provider end |
Instance Attribute Details
#password ⇒ Object
Our configuration for FA access
35 36 37 |
# File 'lib/costagent.rb', line 35 def password @password end |
#subdomain ⇒ Object
Our configuration for FA access
35 36 37 |
# File 'lib/costagent.rb', line 35 def subdomain @subdomain end |
#username ⇒ Object
Our configuration for FA access
35 36 37 |
# File 'lib/costagent.rb', line 35 def username @username end |
Class Method Details
.usd_rate ⇒ Object
This returns the current USD rate from xe.com (or falls back on 1.6 if there is an error)
234 235 236 |
# File 'lib/costagent.rb', line 234 def usd_rate @@rate ||= ((Hpricot(Kernel.open("http://www.xe.com"))/"a").detect { |a| a.attributes["id"] == "USDGBP31" }.children.first.to_s.to_f rescue 1.6) end |
Instance Method Details
#api(resource, parameters = {}) ⇒ Object
This calls the FA API for the specified resource
220 221 222 223 224 |
# File 'lib/costagent.rb', line 220 def api(resource, parameters = {}) res = self.client(resource, parameters).get raise "No response from #{url}!" if res.body.nil? && res.body.empty? Hpricot(res.body) end |
#cache(resource, identifier, reload = false, &block) ⇒ Object
This calls out to the external third party provider for caching
43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/costagent.rb', line 43 def cache(resource, identifier, reload = false, &block) if CostAgent.cache_provider.nil? block.call else if (!reload && CostAgent.cache_provider.exists?(self.subdomain, resource, identifier)) CostAgent.cache_provider.get(self.subdomain, resource, identifier) else CostAgent.cache_provider.set(self.subdomain, resource, identifier, block.call) end end end |
#client(resource, parameters = {}) ⇒ Object
This returns a client ready to query the FA API
227 228 229 230 |
# File 'lib/costagent.rb', line 227 def client(resource, parameters = {}) url = "https://#{self.subdomain}.freeagentcentral.com/#{resource}#{parameters.empty? ? "" : "?" + parameters.collect { |p| p.join("=") }.join("&")}" RestClient::Resource.new(url, self.username, self.password) end |
#earnt(start_date = DateTime.now, end_date = start_date) ⇒ Object
This returns the amount of GBP earnt in the specified timeframe
209 210 211 212 213 214 215 216 217 |
# File 'lib/costagent.rb', line 209 def earnt(start_date = DateTime.now, end_date = start_date) self.timeslips(start_date, end_date).collect do |timeslip| if timeslip.project.currency == "GBP" timeslip.cost else timeslip.cost / CostAgent.usd_rate end end.inject(0) { |sum, i| sum += i } end |
#invoice(id) ⇒ Object
This returns the specific invoice by ID
181 182 183 |
# File 'lib/costagent.rb', line 181 def invoice(id) self.invoices.detect { |i| i.id == id } end |
#invoices(reload = false) ⇒ Object
This returns all invoices
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/costagent.rb', line 145 def invoices(reload = false) self.cache(CostAgent::Invoice, :all, reload) do (self.api("invoices")/"invoice").collect do |invoice| items = (invoice/"invoice-item").collect do |item| price = (item/"price").first.inner_text.to_f quantity = (item/"quantity").first.inner_text.to_f cost = price * quantity project = self.project((item/"project-id").first.inner_text.to_i) InvoiceItem.new( :id => (item/"id").first.inner_text.to_i, :invoice_id => (item/"invoice-id").first.inner_text.to_i, :project_id => project.nil? ? nil : project.id, :project => project, :item_type => (item/"item-type").first.inner_text, :description => (item/"description").first.inner_text, :price => price, :quantity => quantity, :cost => cost) end project = self.project((invoice/"project-id").first.inner_text.to_i) Invoice.new( :id => (invoice/"id").first.inner_text.to_i, :project_id => project.nil? ? nil : project.id, :project => project, :description => (invoice/"description").first.inner_text, :reference => (invoice/"reference").text, :amount => (invoice/"net-value").text.to_f, :status => (invoice/"status").text, :date => DateTime.parse((invoice/"dated-on").text), :due => DateTime.parse((invoice/"due-on").text), :items => items) end end end |
#project(id) ⇒ Object
This returns the specified project
87 88 89 |
# File 'lib/costagent.rb', line 87 def project(id) self.projects("all").detect { |p| p.id == id } end |
#projects(filter = "active", reload = false) ⇒ Object
Returns all projects
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/costagent.rb', line 67 def projects(filter = "active", reload = false) self.cache(CostAgent::Project, filter, reload) do (self.api("projects", {:view => filter})/"project").collect do |project| billing_rate = (project/"normal-billing-rate").text.to_f hours_per_day = (project/"hours-per-day").text.to_f billing_period = (project/"billing-period").text hourly_rate = (billing_period == "hour" ? billing_rate : billing_rate / hours_per_day) daily_rate = (billing_period == "hour" ? billing_rate * hours_per_day : billing_rate) Project.new( :id => (project/"id").text.to_i, :name => (project/"name").text, :currency => (project/"currency").text, :hourly_billing_rate => hourly_rate, :daily_billing_rate => daily_rate, :hours_per_day => hours_per_day) end end end |
#tasks(project_id, reload = false) ⇒ Object
This returns all tasks for the specified project_id
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
# File 'lib/costagent.rb', line 121 def tasks(project_id, reload = false) self.cache(CostAgent::Task, project_id, reload) do (self.api("projects/#{project_id}/tasks")/"task").collect do |task| # Find the project for this task project = self.project((task/"project-id").text.to_i) # Calculate rates billing_rate = (task/"billing-rate").text.to_f billing_period = (task/"billing-period").text hourly_rate = (billing_period == "hour" ? billing_rate : billing_rate / project.hours_per_day) daily_rate = (billing_period == "hour" ? billing_rate * project.hours_per_day : billing_rate) # Build the task out using the task data and the project it's tied to Task.new( :id => (task/"id").text.to_i, :name => (task/"name").text, :project_id => project.id, :project => project, :hourly_billing_rate => hourly_rate, :daily_billing_rate => daily_rate, :billable => (task/"is-billable").text == "true") end end end |
#timeslips(start_date = DateTime.now, end_date = start_date, reload = false) ⇒ Object
This returns all timeslips for the specified date range, with additional cost information
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/costagent.rb', line 92 def timeslips(start_date = DateTime.now, end_date = start_date, reload = false) self.cache(CostAgent::Timeslip, "#{start_date.strftime("%Y-%m-%d")}_#{end_date.strftime("%Y-%m-%d")}", reload) do timeslips = (self.api("timeslips", :view => "#{start_date.strftime("%Y-%m-%d")}_#{end_date.strftime("%Y-%m-%d")}")/"timeslip").collect do |timeslip| # Find the project and hours for this timeslip project = self.project((timeslip/"project-id").text.to_i) if project task = self.tasks(project.id).detect { |t| t.id == (timeslip/"task-id").text.to_i } hours = (timeslip/"hours").text.to_f cost = (task.nil? ? project : task).hourly_billing_rate * hours # Build the timeslip out using the timeslip data and the project it's tied to Timeslip.new( :id => (timeslip/"id").text.to_i, :project_id => project.id, :project => project, :task_id => task.id, :task => task, :hours => hours, :date => DateTime.parse((timeslip/"dated-on").text), :cost => cost, :comment => (timeslip/"comment").text, :status => (timeslip/"status").text) else nil end end - [nil] end end |
#user(reload = false) ⇒ Object
This contains the logged in user information for the configured credentials
186 187 188 189 190 191 192 193 194 |
# File 'lib/costagent.rb', line 186 def user(reload = false) self.cache(CostAgent::User, self.username, reload) do data = self.client("verify").get.headers [User.new( :id => data[:user_id], :permissions => data[:user_permission_level], :company_type => data[:company_type])] end.first end |
#user_id ⇒ Object
This looks up the user ID using the CostAgent credentials
197 198 199 |
# File 'lib/costagent.rb', line 197 def user_id self.user.id end |
#worked(start_date = DateTime.now, end_date = start_date) ⇒ Object
This returns the amount of hours worked
202 203 204 205 206 |
# File 'lib/costagent.rb', line 202 def worked(start_date = DateTime.now, end_date = start_date) self.timeslips(start_date, end_date).collect do |timeslip| timeslip.hours end.inject(0) { |sum, i| sum += i } end |