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 +4 -4
- data/.ruby-version +1 -1
- data/Gemfile +1 -1
- data/HISTORY.md +6 -0
- data/README.md +4 -0
- data/lib/harvest/api/account.rb +5 -2
- data/lib/harvest/api/expenses.rb +17 -11
- data/lib/harvest/api/task_assignments.rb +3 -3
- data/lib/harvest/api/user_assignments.rb +3 -3
- data/lib/harvest/invoice.rb +3 -1
- data/lib/harvest/invoice_category.rb +7 -1
- data/lib/harvest/version.rb +1 -1
- data/spec/functional/invoice_payments_spec.rb +3 -3
- data/spec/functional/invoice_spec.rb +5 -3
- data/spec/functional/reporting_spec.rb +1 -1
- data/spec/functional/time_tracking_spec.rb +1 -1
- data/spec/harvest/invoice_spec.rb +4 -1
- data/spec/spec_helper.rb +6 -1
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 716eb0ff1c8bf452bcc5c27141b506434879091e
|
4
|
+
data.tar.gz: 678f0a9979ebf26642b99394b1758bbf6e3fa003
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2ef6acffd66daf6656bba1596be6da0432b7fd66cda63ee40dd41f41ec08473c760bd339d1f3afe928ebdde7e76cb3920a36d99a8ec83ca6e7ba068466f863ed
|
7
|
+
data.tar.gz: a1924f4e508eab2b6780d08b5f775fbedc34dcfb2aa1cc90d30d1656a7d1ff40a670682fa221dd587241a8886f36755646439bfe2dc25ddaf943d27416d7810c
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.3.4
|
data/Gemfile
CHANGED
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).
|
data/lib/harvest/api/account.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/harvest/api/expenses.rb
CHANGED
@@ -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 << "
|
16
|
+
body << "--__X_ATTACH_BOUNDARY__\r\n"
|
18
17
|
body << %Q{Content-Disposition: form-data; name="expense[receipt]"; filename="#{filename}"\r\n}
|
19
|
-
body << "
|
20
|
-
body << "\r\n
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
|
data/lib/harvest/invoice.rb
CHANGED
@@ -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; "
|
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
|
data/lib/harvest/version.rb
CHANGED
@@ -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 == '
|
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 == '
|
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 =>
|
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:
|
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 == "
|
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 '
|
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
|
data/spec/spec_helper.rb
CHANGED
@@ -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
|
-
|
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:
|
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:
|
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.
|
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:
|