harvested 3.1.1 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: