harvested 0.6.2 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -3,12 +3,15 @@ source :rubygems
3
3
  gem 'httparty'
4
4
  gem 'hashie', '~> 1'
5
5
  gem 'json'
6
+ gem 'yard'
7
+ gem 'redcarpet'
6
8
 
7
9
  group :development, :test do
8
- gem "rspec", "~> 2"
10
+ gem 'rspec', '~> 2'
9
11
  gem 'jruby-openssl', :platform => [:jruby], :require => false
10
- gem "webmock"
11
- gem "vcr"
12
+ gem 'webmock'
13
+ gem 'vcr'
12
14
  gem 'jeweler', :require => false
13
15
  gem 'debugger'
16
+ gem 'factory_girl'
14
17
  end
data/HISTORY CHANGED
@@ -1,16 +1,20 @@
1
+ 0.6.3 - October 24, 2012
2
+ * Adds task activation (Thanks Mark Rickert - @markrickert)
3
+ * Adds basic invoice support (Thanks Jeffrey Lee - @jlee42)
4
+ * Adds basic invoice payment support (Thanks Adam Doeler - @releod)
1
5
  0.6.2 - August 25, 2012
2
- * Fixing Mash constructor errors
6
+ * Fixes Mash constructor errors
3
7
  0.6.1 - August 22, 2012
4
8
  * Adds options to "all" finder (thanks Mikel Lindsaar)
5
9
  * Adds Unauthorized error type (thanks @bcobb)
6
10
  0.6.0 - August 22, 2012
7
11
  * Replaces Dash with Mash
8
12
  0.5.3 - August 21, 2012
9
- * Adding new fields has_timesheet_2012_beta and timesheet_2012_beta_control_group to Users (thanks Aldric Giacomoni)
13
+ * Adds new fields has_timesheet_2012_beta and timesheet_2012_beta_control_group to Users (thanks Aldric Giacomoni)
10
14
  0.5.2 - August 17, 2012
11
- * Adding new field password_change_required to Users
15
+ * Adds new field password_change_required to Users
12
16
  0.5.1 - June 27, 2012
13
- * Updating README and harvested_credentials.example.yml with warnings about normal accounts
17
+ * Updates README and harvested_credentials.example.yml with warnings about normal accounts
14
18
  0.5.0 - June 18, 2012
15
19
  * Bugfixes on User: https://github.com/zmoazeni/harvested/pull/26
16
20
  * Bugfixes on TimeEntry: https://github.com/zmoazeni/harvested/pull/25
@@ -18,30 +22,30 @@
18
22
  * Large rewrite of codebase
19
23
  * Rewrite of library to use JSON instead of XML
20
24
  * Rewrite of tests to use rspec instead of cucumber
21
- * Brought in VCR to help cache test responses
22
- * Implemented more reporting functionality
23
- * Implemented most of the invoice functionality. Creating/Editing/Updating is disabled due to Harvest API issue
24
- * Consolidated various forks (thanks Chris Ritterdorf, bricooke, Michelle Moon Lee, tkwong, Steve McKinney, and others that helped but aren't in the git log!)
25
+ * Brings in VCR to help cache test responses
26
+ * Implements more reporting functionality
27
+ * Implements most of the invoice functionality. Creating/Editing/Updating is disabled due to Harvest API issue
28
+ * Consolidates various forks (thanks Chris Ritterdorf, bricooke, Michelle Moon Lee, tkwong, Steve McKinney, and others that helped but aren't in the git log!)
25
29
  * Tests passing against MRI 1.8, 1.9, JRuby, and Rubinius
26
30
  0.3.3 - January 27, 2010
27
- * Adding fields to TaskAssignment (Quilted)
31
+ * Adds fields to TaskAssignment (Quilted)
28
32
  0.3.2 - January 27, 2010
29
- * Add of_user support to Time Entry (Benjamin Wong - tkwong)
33
+ * Adds of_user support to Time Entry (Benjamin Wong - tkwong)
30
34
  0.3.1 - October 14, 2010
31
- * Updated docs removing :ssl => false since Harvest released SSL to everyone
32
- * Added department to the User model (Steve McKinney - )
35
+ * Updates docs removing :ssl => false since Harvest released SSL to everyone
36
+ * Adds department to the User model (Steve McKinney - )
33
37
  0.3.0 - April 11, 2010
34
- * Added users
35
- * Added clients
36
- * Added contacts
37
- * Added expense categories
38
- * Added expenses
39
- * Added projects
40
- * Added tasks
41
- * Added task assignments
42
- * Added user assignments
43
- * Added time tracking
44
- * Added reporting
45
- * Added account information
46
- * Added common errors
47
- * Added Harvest#hardy_client
38
+ * Adds users
39
+ * Adds clients
40
+ * Adds contacts
41
+ * Adds expense categories
42
+ * Adds expenses
43
+ * Adds projects
44
+ * Adds tasks
45
+ * Adds task assignments
46
+ * Adds user assignments
47
+ * Adds time tracking
48
+ * Adds reporting
49
+ * Adds account information
50
+ * Adds common errors
51
+ * Adds Harvest#hardy_client
data/README.md CHANGED
@@ -10,7 +10,7 @@ This is a Ruby wrapper for the [Harvest API](http://www.getharvest.com/api).
10
10
 
11
11
  ```ruby
12
12
  harvest = Harvest.client('yoursubdomain', 'yourusername', 'yourpassword')
13
- harvest.projects() # list out projects
13
+ harvest.projects.all # list out projects
14
14
 
15
15
  client = Harvest::Client.new(:name => "Billable Company LTD.")
16
16
  client = harvest.clients.create(client)
@@ -41,7 +41,7 @@ Using `Harvested#client` your code needs to handle all these situations. However
41
41
 
42
42
  ```ruby
43
43
  harvest = Harvest.hardy_client('yoursubdomain', 'yourusername', 'yourpassword')
44
- harvest.projects() # This will wait for the Rate Limit reset if you have gone over your limit
44
+ harvest.projects.all # This will wait for the Rate Limit reset if you have gone over your limit
45
45
  ```
46
46
 
47
47
  ## Ruby support
@@ -73,7 +73,7 @@ Note on running tests: most specs run against a live Harvest account. To run the
73
73
 
74
74
  **DO NOT USE YOUR NORMAL CREDENTIALS IN `/spec/support/harvest_credentials.yml`!!!** The test suite blasts all the data before running (similiar to DatabaseCleaner).
75
75
 
76
- The tests use [VCR](https://github.com/myronmarston/vcr) to cache the test responses. This is a great boon for running the tests offline. While uncommon, sometimes the Harvest API will send an erroneous response and VCR will cache it, then subsequent runs will use the incorrect cached response. In order to ignore VCR you can run the specs by passing CACHE=false (e.g. `CACHE=false bundle rake spec`).
76
+ The tests use [VCR](https://github.com/myronmarston/vcr) to cache the API responses. This is a great boon for running the tests offline. While uncommon, sometimes the Harvest API will send an erroneous response, VCR will cache it, and subsequent runs will use the incorrect cached response. In order to clear the cached responses, you can run the specs with the `VCR_REFRESH` environmental variable set to true (e.g. `VCR_REFRESH=true bundle exec rake spec`).
77
77
 
78
78
  Using [rvm](https://rvm.beginrescueend.com/) you can run the tests against the popular ruby runtimes by running:
79
79
 
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ $:.unshift(File.dirname(__FILE__))
2
+
1
3
  require 'rubygems'
2
4
  require 'rake'
3
5
  require "bundler/setup"
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.2
1
+ 0.6.3
@@ -0,0 +1,93 @@
1
+ # File: project_create_script.rb
2
+ # Date Created: 2012-10-08
3
+ # Author(s): Mark Rickert (mjar81@gmail.com) / Skookum Digital Works - http://skookum.com
4
+ #
5
+ # Description: This example script takes user input from the command line and
6
+ # creates a project based the selected options. It then assigns tasks from Harvest
7
+ # to the project based on an array. After the tasks are added, it addes all the
8
+ # currently active users to the project.
9
+
10
+ require "harvested"
11
+
12
+ subdomain = 'yoursubdomain'
13
+ username = 'yourusername'
14
+ password = 'yourpassword'
15
+
16
+ harvest = Harvest.hardy_client(subdomain, username, password)
17
+
18
+ puts "\nPlease search for a client by typing part of their name:"
19
+ cname = gets.chomp
20
+
21
+ # Filter out all the clients that don't match the typed string
22
+ client_search_results = harvest.clients.all.select { |c| c.name.downcase.include?(cname) }
23
+
24
+ case client_search_results.length
25
+ when 0
26
+ puts "No client found. Please try again.\n"
27
+ abort
28
+ when 1
29
+ # Result is exactly 1. We got the client.
30
+ client_index = 0
31
+ when 1..15
32
+ # Have the user select from a list of clients.
33
+ puts "Please select from the following results:\n"
34
+ client_search_results.each_with_index do |c, i|
35
+ puts " #{i+1}. #{c.name}"
36
+ end
37
+
38
+ client_index = gets.chomp.to_i - 1
39
+ else
40
+ puts "Too many client matches. Please try a more specific search term.\n"
41
+ abort
42
+ end
43
+
44
+ client = client_search_results[client_index]
45
+
46
+ puts "\nClient found: #{client.name}\n"
47
+ puts "Please enter the project name:"
48
+ project_name = gets.chomp
49
+
50
+ puts "\nIs this project billable? (y/n)"
51
+ billable = gets.chomp.downcase == "y"
52
+
53
+ puts "\nAny project notes? (hit 'return' for none)"
54
+ notes = gets.chomp
55
+
56
+ puts "\nWhat sorts of tasks does this project need? Type \"p\" for project tasks or \"s\" for sales tasks.\n"
57
+ task_types = gets.chomp
58
+
59
+ # Determine what task list to use based on the project type.
60
+ if task_types.downcase == "p"
61
+ # These are names of tasks that should already exist in Harvest
62
+ use_tasks = ["Client Communication", "Planning", "Design", "Development", "Testing/QA", "Documentation", "Deployment"]
63
+ else
64
+ use_tasks = ["Sales Calls", "Client Meetings", "Travel", "Estimating"]
65
+ end
66
+
67
+ # Filter the list of actual tasks we want to add to the project.
68
+ tasks = harvest.tasks.all.select { |t| use_tasks.include?(t.name) }
69
+
70
+ # Create the project
71
+ puts "Creating new project: \"#{project_name}\" for client: #{client.name}\n"
72
+ project = Harvest::Project.new(:name => project_name, :client_id => client.id, :billable => billable, :notes => notes)
73
+ project = harvest.projects.create(project)
74
+
75
+ # Add all the project tasks to the project
76
+ tasks.each do |t|
77
+ puts " Adding Task: #{t.name}"
78
+ task_assignment = Harvest::TaskAssignment.new(:task_id => t.id, :project_id => project.id)
79
+ task_assignment = harvest.task_assignments.create(task_assignment)
80
+ end
81
+
82
+ puts
83
+ # Add every active user to the project.
84
+ harvest.users.all.each do |u|
85
+ next unless u.is_active?
86
+
87
+ puts " Adding User: #{u.first_name} #{u.last_name}"
88
+ user_assignment = Harvest::UserAssignment.new(:user_id => u.id, :project_id => project.id)
89
+ harvest.user_assignments.create(user_assignment)
90
+ end
91
+
92
+ puts "\nProject successfully created."
93
+ puts "You can find the project here: http://#{subdomain}.harvestapp.com/projects/#{project.id}/edit\n\n"
data/harvested.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "harvested"
8
- s.version = "0.6.2"
8
+ s.version = "0.6.3"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Zach Moazeni"]
12
- s.date = "2012-08-25"
12
+ s.date = "2012-10-24"
13
13
  s.description = "Harvested wraps the Harvest API concisely without the use of Rails dependencies. More information about the Harvest API can be found on their website (http://www.getharvest.com/api). For support hit up the Mailing List (http://groups.google.com/group/harvested)"
14
14
  s.email = "zach.moazeni@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -27,6 +27,7 @@ Gem::Specification.new do |s|
27
27
  "VERSION",
28
28
  "examples/basics.rb",
29
29
  "examples/clear_account.rb",
30
+ "examples/project_create_script.rb",
30
31
  "examples/task_assignments.rb",
31
32
  "examples/user_assignments.rb",
32
33
  "harvested.gemspec",
@@ -41,6 +42,7 @@ Gem::Specification.new do |s|
41
42
  "lib/harvest/api/expense_categories.rb",
42
43
  "lib/harvest/api/expenses.rb",
43
44
  "lib/harvest/api/invoice_categories.rb",
45
+ "lib/harvest/api/invoice_payments.rb",
44
46
  "lib/harvest/api/invoices.rb",
45
47
  "lib/harvest/api/projects.rb",
46
48
  "lib/harvest/api/reports.rb",
@@ -61,6 +63,7 @@ Gem::Specification.new do |s|
61
63
  "lib/harvest/hardy_client.rb",
62
64
  "lib/harvest/invoice.rb",
63
65
  "lib/harvest/invoice_category.rb",
66
+ "lib/harvest/invoice_payment.rb",
64
67
  "lib/harvest/line_item.rb",
65
68
  "lib/harvest/model.rb",
66
69
  "lib/harvest/project.rb",
@@ -72,11 +75,13 @@ Gem::Specification.new do |s|
72
75
  "lib/harvest/user.rb",
73
76
  "lib/harvest/user_assignment.rb",
74
77
  "lib/harvested.rb",
78
+ "spec/factories.rb",
75
79
  "spec/functional/account_spec.rb",
76
80
  "spec/functional/clients_spec.rb",
77
81
  "spec/functional/errors_spec.rb",
78
82
  "spec/functional/expenses_spec.rb",
79
83
  "spec/functional/hardy_client_spec.rb",
84
+ "spec/functional/invoice_payments_spec.rb",
80
85
  "spec/functional/invoice_spec.rb",
81
86
  "spec/functional/project_spec.rb",
82
87
  "spec/functional/reporting_spec.rb",
@@ -87,6 +92,7 @@ Gem::Specification.new do |s|
87
92
  "spec/harvest/credentials_spec.rb",
88
93
  "spec/harvest/expense_category_spec.rb",
89
94
  "spec/harvest/expense_spec.rb",
95
+ "spec/harvest/invoice_payment_spec.rb",
90
96
  "spec/harvest/invoice_spec.rb",
91
97
  "spec/harvest/project_spec.rb",
92
98
  "spec/harvest/task_assignment_spec.rb",
@@ -102,7 +108,7 @@ Gem::Specification.new do |s|
102
108
  ]
103
109
  s.homepage = "http://github.com/zmoazeni/harvested"
104
110
  s.require_paths = ["lib"]
105
- s.rubygems_version = "1.8.23"
111
+ s.rubygems_version = "1.8.24"
106
112
  s.summary = "A Ruby Wrapper for the Harvest API http://www.getharvest.com/"
107
113
 
108
114
  if s.respond_to? :specification_version then
@@ -112,33 +118,42 @@ Gem::Specification.new do |s|
112
118
  s.add_runtime_dependency(%q<httparty>, [">= 0"])
113
119
  s.add_runtime_dependency(%q<hashie>, ["~> 1"])
114
120
  s.add_runtime_dependency(%q<json>, [">= 0"])
121
+ s.add_runtime_dependency(%q<yard>, [">= 0"])
122
+ s.add_runtime_dependency(%q<redcarpet>, [">= 0"])
115
123
  s.add_development_dependency(%q<rspec>, ["~> 2"])
116
124
  s.add_development_dependency(%q<jruby-openssl>, [">= 0"])
117
125
  s.add_development_dependency(%q<webmock>, [">= 0"])
118
126
  s.add_development_dependency(%q<vcr>, [">= 0"])
119
127
  s.add_development_dependency(%q<jeweler>, [">= 0"])
120
128
  s.add_development_dependency(%q<debugger>, [">= 0"])
129
+ s.add_development_dependency(%q<factory_girl>, [">= 0"])
121
130
  else
122
131
  s.add_dependency(%q<httparty>, [">= 0"])
123
132
  s.add_dependency(%q<hashie>, ["~> 1"])
124
133
  s.add_dependency(%q<json>, [">= 0"])
134
+ s.add_dependency(%q<yard>, [">= 0"])
135
+ s.add_dependency(%q<redcarpet>, [">= 0"])
125
136
  s.add_dependency(%q<rspec>, ["~> 2"])
126
137
  s.add_dependency(%q<jruby-openssl>, [">= 0"])
127
138
  s.add_dependency(%q<webmock>, [">= 0"])
128
139
  s.add_dependency(%q<vcr>, [">= 0"])
129
140
  s.add_dependency(%q<jeweler>, [">= 0"])
130
141
  s.add_dependency(%q<debugger>, [">= 0"])
142
+ s.add_dependency(%q<factory_girl>, [">= 0"])
131
143
  end
132
144
  else
133
145
  s.add_dependency(%q<httparty>, [">= 0"])
134
146
  s.add_dependency(%q<hashie>, ["~> 1"])
135
147
  s.add_dependency(%q<json>, [">= 0"])
148
+ s.add_dependency(%q<yard>, [">= 0"])
149
+ s.add_dependency(%q<redcarpet>, [">= 0"])
136
150
  s.add_dependency(%q<rspec>, ["~> 2"])
137
151
  s.add_dependency(%q<jruby-openssl>, [">= 0"])
138
152
  s.add_dependency(%q<webmock>, [">= 0"])
139
153
  s.add_dependency(%q<vcr>, [">= 0"])
140
154
  s.add_dependency(%q<jeweler>, [">= 0"])
141
155
  s.add_dependency(%q<debugger>, [">= 0"])
156
+ s.add_dependency(%q<factory_girl>, [">= 0"])
142
157
  end
143
158
  end
144
159
 
@@ -3,24 +3,24 @@ module Harvest
3
3
  class InvoiceCategories < Base
4
4
  api_model Harvest::InvoiceCategory
5
5
  include Harvest::Behavior::Crud
6
-
6
+
7
7
  def find(*)
8
8
  raise "find is unsupported for InvoiceCategories"
9
9
  end
10
-
10
+
11
11
  def create(model)
12
12
  model = api_model.wrap(model)
13
13
  response = request(:post, credentials, "#{api_model.api_path}", :body => model.to_json)
14
14
  id = response.headers["location"].match(/\/.*\/(\d+)/)[1]
15
15
  all.detect {|c| c.id == id.to_i }
16
16
  end
17
-
17
+
18
18
  def update(model, user = nil)
19
19
  model = api_model.wrap(model)
20
20
  request(:put, credentials, "#{api_model.api_path}/#{model.to_i}", :body => model.to_json, :query => of_user_query(user))
21
21
  all.detect {|c| c.id == model.id }
22
22
  end
23
-
23
+
24
24
  end
25
25
  end
26
- end
26
+ end
@@ -0,0 +1,31 @@
1
+ module Harvest
2
+ module API
3
+ class InvoicePayments < Base
4
+ api_model Harvest::InvoicePayment
5
+ include Harvest::Behavior::Crud
6
+
7
+ def all(invoice)
8
+ response = request(:get, credentials, "/invoices/#{invoice.to_i}/payments")
9
+ api_model.parse(response.parsed_response)
10
+ end
11
+
12
+ def find(invoice, payment)
13
+ response = request(:get, credentials, "/invoices/#{invoice.to_i}/payments/#{payment.to_i}")
14
+ api_model.parse(response.parsed_response).first
15
+ end
16
+
17
+ def create(payment)
18
+ payment = api_model.wrap(payment)
19
+ response = request(:post, credentials, "/invoices/#{payment.invoice_id}/payments", :body => payment.to_json)
20
+ id = response.headers["location"].match(/\/.*\/(\d+)\/.*\/(\d+)/)[2]
21
+ find(payment.invoice_id, id)
22
+ end
23
+
24
+ def delete(payment)
25
+ request(:delete, credentials, "/invoices/#{payment.invoice_id}/payments/#{payment.to_i}")
26
+ payment.id
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -3,14 +3,33 @@ module Harvest
3
3
  class Invoices < Base
4
4
  api_model Harvest::Invoice
5
5
  include Harvest::Behavior::Crud
6
-
7
- def create(*)
8
- raise "Creating and updating invoices are not implemented due to API issues"
9
- end
10
-
11
- def update(*)
12
- raise "Creating and updating invoices are not implemented due to API issues"
6
+
7
+ # == Retrieves invoices
8
+ #
9
+ # == Available options
10
+ # - :status - invoices by status
11
+ # - :page
12
+ # - :updated_since
13
+ # - :timeframe (must be a nested hash with :to and :from)
14
+ #
15
+ # @overload all()
16
+ # @overload all(options)
17
+ # @param [Hash] filtering options
18
+ #
19
+ # @return [Array<Harvest::Invoice>] an array of invoices
20
+ def all(options = {})
21
+ query = {}
22
+ query[:status] = options[:status] if options[:status]
23
+ query[:page] = options[:page] if options[:page]
24
+ query[:updated_since] = options[:updated_since] if options[:updated_since]
25
+ if options[:timeframe]
26
+ query[:from] = options[:timeframe][:from]
27
+ query[:to] = options[:timeframe][:to]
28
+ end
29
+
30
+ response = request(:get, credentials, "/invoices", :query => query)
31
+ api_model.parse(response.parsed_response)
13
32
  end
14
33
  end
15
34
  end
16
- end
35
+ end
@@ -4,6 +4,33 @@ module Harvest
4
4
  api_model Harvest::Task
5
5
 
6
6
  include Harvest::Behavior::Crud
7
+
8
+ # Deactivating tasks is not yet supported by the Harvest API.
9
+
10
+ # Deactivates the task. Does nothing if the task is already deactivated
11
+ #
12
+ # @param [Harvest::Task] task the task you want to deactivate
13
+ # @return [Harvest::Task] the deactivated task
14
+ #def deactivate(task)
15
+ # if task.active?
16
+ # request(:post, credentials, "#{api_model.api_path}/#{task.to_i}/deactivate", :headers => {'Content-Length' => '0'})
17
+ # task.active = false
18
+ # end
19
+ # task
20
+ #end
21
+
22
+ # Activates the task. Does nothing if the task is already activated
23
+ #
24
+ # @param [Harvest::Task] task the task you want to activate
25
+ # @return [Harvest::Task] the activated task
26
+ def activate(task)
27
+ if !task.active?
28
+ request(:post, credentials, "#{api_model.api_path}/#{task.to_i}/activate", :headers => {'Content-Length' => '0'})
29
+ task.active = true
30
+ end
31
+ task
32
+ end
33
+
7
34
  end
8
35
  end
9
36
  end