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 +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:
|