harvested 3.1.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 85f9194a4556d2e4f55bd7ed336a18393b3b75ba
4
- data.tar.gz: b63cbb54de97e276cbaa9f4829f0756d8ceb74aa
3
+ metadata.gz: 716eb0ff1c8bf452bcc5c27141b506434879091e
4
+ data.tar.gz: 678f0a9979ebf26642b99394b1758bbf6e3fa003
5
5
  SHA512:
6
- metadata.gz: dc40190d2ee5dde42dc4f7815306dc341edc5e0b503e828ed63f333739113f2b6c1087b50990978717979e9780d0a5bec24dd9a52062d9887177be2b1b316b11
7
- data.tar.gz: f43828a1bbde4e7ca2443d97d741d7eb307f0c21521e80d24ac2e0a37b36a6df40c6878347e14a58165c22d8d7643445cd7b01973ac80d7e1a3bbb51fe3137c3
6
+ metadata.gz: 2ef6acffd66daf6656bba1596be6da0432b7fd66cda63ee40dd41f41ec08473c760bd339d1f3afe928ebdde7e76cb3920a36d99a8ec83ca6e7ba068466f863ed
7
+ data.tar.gz: a1924f4e508eab2b6780d08b5f775fbedc34dcfb2aa1cc90d30d1656a7d1ff40a670682fa221dd587241a8886f36755646439bfe2dc25ddaf943d27416d7810c
@@ -1 +1 @@
1
- 2.1.1
1
+ 2.3.4
data/Gemfile CHANGED
@@ -4,7 +4,7 @@ gemspec
4
4
 
5
5
  group :development, :test do
6
6
  gem 'bundler', '>= 1.6.2'
7
- gem 'rake'
7
+ gem 'rake', '< 11'
8
8
 
9
9
  gem 'rspec', '~> 2'
10
10
  gem 'jruby-openssl', :platform => [:jruby], :require => false
data/HISTORY.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 4.0.0 - Apr 21, 2017
2
+ * Invoices need to be told explicitly to send line items to the server with a new attribute: `update_line_items`.
3
+ * Task/UserAssignment endpoints can now be passed query params. (Thanks Brendan Loudermilk - @bloudermilk and Nick Giancola @patbenatar)
4
+ * Expense Attachments work again. (Thanks Peter - @toothfairy)
5
+ * Company information is now included when retreiving info from `who_am_i`. (Thanks André Arko - @indirect)
6
+
1
7
  ## 3.1.0 - Aug 29, 2014
2
8
  * Allows Invoice Messages to be created (Thanks Thomas Balthazar - @tbalthazar)
3
9
 
data/README.md CHANGED
@@ -1,3 +1,7 @@
1
+ # Harvested needs maintainers!! If you are interested in maintaining Harvested, create an issue! Until someone volunteers to help, development and bugfixes will lag. #
2
+
3
+ Current project status: abandoned
4
+
1
5
  # Harvested: A Ruby Harvest API
2
6
 
3
7
  This is a Ruby wrapper for the [Harvest API](http://www.getharvest.com/api).
@@ -15,8 +15,11 @@ module Harvest
15
15
  # @return [Harvest::User]
16
16
  def who_am_i
17
17
  response = request(:get, credentials, '/account/who_am_i')
18
- Harvest::User.parse(response.body).first
18
+ parsed = JSON.parse(response.body)
19
+ Harvest::User.parse(parsed).first.tap do |user|
20
+ user.company = parsed["company"]
21
+ end
19
22
  end
20
23
  end
21
24
  end
22
- end
25
+ end
@@ -2,26 +2,32 @@ module Harvest
2
2
  module API
3
3
  class Expenses < Base
4
4
  api_model Harvest::Expense
5
-
5
+
6
6
  include Harvest::Behavior::Crud
7
-
7
+
8
8
  def all(date = ::Time.now, user = nil)
9
9
  date = ::Time.parse(date) if String === date
10
10
  response = request(:get, credentials, "#{api_model.api_path}/#{date.yday}/#{date.year}", :query => of_user_query(user))
11
11
  api_model.parse(response.parsed_response)
12
12
  end
13
-
14
- # This is currently broken, but will come back to it
13
+
15
14
  def attach(expense, filename, receipt)
16
15
  body = ""
17
- body << "------------------------------b7edea381b46\r\n"
16
+ body << "--__X_ATTACH_BOUNDARY__\r\n"
18
17
  body << %Q{Content-Disposition: form-data; name="expense[receipt]"; filename="#{filename}"\r\n}
19
- body << "Content-Type: image/png\r\n"
20
- body << "\r\n#{receipt.read}\r\n"
21
- body << "------------------------------b7edea381b46\r\n"
22
-
23
- request(:post, credentials, "#{api_model.api_path}/#{expense.to_i}/receipt", :headers => {'Content-Type' => 'multipart/form-data; boundary=------------------------------b7edea381b46'}, :body => body)
18
+ body << "\r\n#{receipt.read}"
19
+ body << "\r\n--__X_ATTACH_BOUNDARY__--\r\n\r\n"
20
+
21
+ request(
22
+ :post,
23
+ credentials,
24
+ "#{api_model.api_path}/#{expense.to_i}/receipt",
25
+ :headers => {
26
+ 'Content-Type' => 'multipart/form-data; charset=utf-8; boundary=__X_ATTACH_BOUNDARY__',
27
+ 'Content-Length' => body.length.to_s,
28
+ },
29
+ :body => body)
24
30
  end
25
31
  end
26
32
  end
27
- end
33
+ end
@@ -2,8 +2,8 @@ module Harvest
2
2
  module API
3
3
  class TaskAssignments < Base
4
4
 
5
- def all(project)
6
- response = request(:get, credentials, "/projects/#{project.to_i}/task_assignments")
5
+ def all(project, query = {})
6
+ response = request(:get, credentials, "/projects/#{project.to_i}/task_assignments", {query: query})
7
7
  Harvest::TaskAssignment.parse(response.parsed_response)
8
8
  end
9
9
 
@@ -31,4 +31,4 @@ module Harvest
31
31
  end
32
32
  end
33
33
  end
34
- end
34
+ end
@@ -1,9 +1,9 @@
1
1
  module Harvest
2
2
  module API
3
3
  class UserAssignments < Base
4
-
5
- def all(project)
6
- response = request(:get, credentials, "/projects/#{project.to_i}/user_assignments")
4
+
5
+ def all(project, query = {})
6
+ response = request(:get, credentials, "/projects/#{project.to_i}/user_assignments", {query: query})
7
7
  Harvest::UserAssignment.parse(response.parsed_response)
8
8
  end
9
9
 
@@ -42,6 +42,7 @@ module Harvest
42
42
  api_path '/invoices'
43
43
 
44
44
  attr_reader :line_items
45
+ attr_accessor :update_line_items
45
46
 
46
47
  def self.parse(json)
47
48
  parsed = String === json ? JSON.parse(json) : json
@@ -60,6 +61,7 @@ module Harvest
60
61
  self.line_items = args.delete("csv_line_items")
61
62
  self.line_items = args.delete("line_items")
62
63
  self.line_items = [] if self.line_items.nil?
64
+ self.update_line_items = args.delete("update_line_items")
63
65
  end
64
66
  super
65
67
  end
@@ -77,7 +79,7 @@ module Harvest
77
79
 
78
80
  def as_json(*options)
79
81
  json = super(*options)
80
- json[json_root]["csv_line_items"] = encode_csv(@line_items)
82
+ json[json_root]["csv_line_items"] = encode_csv(@line_items) if update_line_items
81
83
  json
82
84
  end
83
85
 
@@ -3,7 +3,13 @@ module Harvest
3
3
  include Harvest::Model
4
4
 
5
5
  api_path '/invoice_item_categories'
6
- def self.json_root; "category"; end
6
+ def self.json_root; "invoice_item_category"; end
7
7
 
8
+ class << self
9
+ def parse(json)
10
+ parsed = String === json ? JSON.parse(json) : json
11
+ Array.wrap(parsed).map {|attrs| new(attrs["invoice_category"])}
12
+ end
13
+ end
8
14
  end
9
15
  end
@@ -1,3 +1,3 @@
1
1
  module Harvest
2
- VERSION = "3.1.1"
2
+ VERSION = "4.0.0"
3
3
  end
@@ -18,7 +18,7 @@ describe 'harvest invoice payments' do
18
18
  it 'allows adding, and removing invoice payments' do
19
19
  cassette('invoice_payment2') do
20
20
  client = harvest.clients.create(FactoryGirl.attributes_for(:client))
21
- invoice = harvest.invoices.create(FactoryGirl.attributes_for(:invoice, :client_id => client.id))
21
+ invoice = harvest.invoices.create(FactoryGirl.attributes_for(:invoice, :client_id => client.id, update_line_items: true))
22
22
 
23
23
  half_amount = (invoice.amount.to_f / 2)
24
24
 
@@ -26,7 +26,7 @@ describe 'harvest invoice payments' do
26
26
  payment1 = harvest.invoice_payments.create(payment1)
27
27
 
28
28
  invoice = harvest.invoices.find(invoice.id)
29
- invoice.state.should == 'partial'
29
+ invoice.state.should == 'draft'
30
30
 
31
31
  payment2 = Harvest::InvoicePayment.new(FactoryGirl.attributes_for(:invoice_payment, :invoice_id => invoice.id, :amount => half_amount))
32
32
  payment2 = harvest.invoice_payments.create(payment2)
@@ -38,7 +38,7 @@ describe 'harvest invoice payments' do
38
38
  harvest.invoice_payments.all(invoice).should be_empty
39
39
 
40
40
  invoice = harvest.invoices.find(invoice.id)
41
- invoice.state.should == 'open'
41
+ invoice.state.should == 'draft'
42
42
  end
43
43
  end
44
44
  end
@@ -19,7 +19,7 @@ describe 'harvest invoices' do
19
19
  cassette('invoice2') do
20
20
  client = harvest.clients.create(FactoryGirl.attributes_for(:client))
21
21
 
22
- invoice = Harvest::Invoice.new(FactoryGirl.attributes_for(:invoice, :client_id => client.id))
22
+ invoice = Harvest::Invoice.new(FactoryGirl.attributes_for(:invoice, :client_id => client.id, update_line_items: true))
23
23
  invoice = harvest.invoices.create(invoice)
24
24
 
25
25
  invoice.subject.should == "Invoice for Joe's Stream Cleaning"
@@ -28,6 +28,7 @@ describe 'harvest invoices' do
28
28
 
29
29
  invoice.subject = "Updated Invoice for Joe"
30
30
  invoice.line_items << FactoryGirl.build(:line_item)
31
+ invoice.update_line_items = true
31
32
 
32
33
  invoice = harvest.invoices.update(invoice)
33
34
  invoice.subject.should == "Updated Invoice for Joe"
@@ -70,7 +71,8 @@ describe 'harvest invoices' do
70
71
  "kind" => "free_form",
71
72
  "import_hours" => "no",
72
73
  "import_expenses" => "no",
73
- "line_items" => [Harvest::LineItem.new("kind" => "Service", "description" => "One item", "quantity" => 200, "unit_price" => "12.00")]
74
+ "line_items" => [Harvest::LineItem.new("kind" => "Service", "description" => "One item", "quantity" => 200, "unit_price" => "12.00")],
75
+ "update_line_items" => true
74
76
  )
75
77
  invoice = harvest.invoices.create(invoice)
76
78
 
@@ -96,7 +98,7 @@ describe 'harvest invoices' do
96
98
  invoices = harvest.invoices.all(:status => 'draft', :page => 1)
97
99
  invoices.count.should == 1
98
100
 
99
- invoices = harvest.invoices.all(:timeframe => {:from => Date.today, :to => Date.today})
101
+ invoices = harvest.invoices.all(:timeframe => {:from => '2014-01-01', :to => '2014-01-01'})
100
102
  invoices.count.should == 1
101
103
 
102
104
  invoices = harvest.invoices.all(:timeframe => {:from => '19690101', :to => '19690101'})
@@ -46,7 +46,7 @@ describe 'harvest reporting' do
46
46
 
47
47
  entry3_time = Time.now.utc
48
48
  entry3 = harvest.time.create(notes: "test entry for checking updated_since", hours: 5, spent_at: "2009/12/15", task_id: task1.id, project_id: project1.id, of_user: user.id)
49
- entries = harvest.reports.time_by_user(user, Time.utc(2009, 12, 10), Time.utc(2009,12,30), {project: project1, updated_since: entry3_time})
49
+ entries = harvest.reports.time_by_user(user, Time.utc(2009, 12, 10), Time.utc(2009,12,30), {project: project1, updated_since: Time.parse(entry3.updated_at) - 1})
50
50
  entries.first.should == entry3
51
51
  entries.size.should == 1
52
52
 
@@ -76,7 +76,7 @@ describe 'harvest time tracking' do
76
76
 
77
77
  trackable_projects = harvest.time.trackable_projects
78
78
  trackable_project = trackable_projects.find {|p| p.name == "Bobby's Trackable Project" }
79
- trackable_projects.first.client.should == "Bobbys Coffee Shop"
79
+ trackable_projects.first.client.should == "Bobby's Coffee Shop"
80
80
  trackable_projects.first.tasks.first.name.should == "A billable task for Bobby"
81
81
  end
82
82
  end
@@ -36,10 +36,13 @@ describe Harvest::Invoice do
36
36
  end
37
37
 
38
38
  context "as_json" do
39
- it 'encodes line items csv' do
39
+ it 'only updates line items remotely if it the invoice is told to' do
40
40
  invoice = Harvest::Invoice.new(:line_items => "kind,description,quantity,unit_price,amount,taxed,taxed2,project_id\nService,Abc,200,12.00,2400.0,false,false,\nService,def,1.00,20.00,20.0,false,false,\n")
41
41
  invoice.line_items.count.should == 2
42
42
  invoice.line_items.first.kind.should == "Service"
43
+ invoice.as_json["invoice"].keys.should_not include("csv_line_items")
44
+
45
+ invoice.update_line_items = true
43
46
  invoice.as_json["invoice"]["csv_line_items"].should == "kind,description,quantity,unit_price,amount,taxed,taxed2,project_id\nService,Abc,200,12.00,2400.0,false,false,\nService,def,1.00,20.00,20.0,false,false,\n"
44
47
  end
45
48
  end
@@ -14,6 +14,7 @@ VCR.configure do |c|
14
14
  # force cassettes to re_record when we pass VCR_REFRESH=true
15
15
  re_record_interval: ENV['VCR_REFRESH'] == 'true' ? 0 : nil
16
16
  }
17
+ c.allow_http_connections_when_no_cassette = true
17
18
  end
18
19
 
19
20
  FactoryGirl.find_definitions
@@ -33,7 +34,11 @@ RSpec.configure do |config|
33
34
  end
34
35
 
35
36
  def cassette(*args)
36
- VCR.use_cassette(*args) do
37
+ if ENV['USE_VCR']
38
+ VCR.use_cassette(*args) do
39
+ yield
40
+ end
41
+ else
37
42
  yield
38
43
  end
39
44
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: harvested
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.1
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zach Moazeni
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-16 00:00:00.000000000 Z
11
+ date: 2017-04-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -177,7 +177,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
177
177
  version: '0'
178
178
  requirements: []
179
179
  rubyforge_project:
180
- rubygems_version: 2.2.2
180
+ rubygems_version: 2.5.2
181
181
  signing_key:
182
182
  specification_version: 4
183
183
  summary: A Ruby Wrapper for the Harvest API http://www.getharvest.com/
@@ -215,4 +215,3 @@ test_files:
215
215
  - spec/support/harvest_credentials.example.yml
216
216
  - spec/support/harvested_helpers.rb
217
217
  - spec/support/json_examples.rb
218
- has_rdoc: