harvesting 0.1.0 → 0.5.1
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/.env.sample +10 -0
- data/.gitignore +13 -0
- data/.travis.yml +7 -2
- data/Dockerfile +6 -0
- data/README.md +173 -32
- data/RELEASE_NOTES.md +80 -0
- data/TODO.md +1 -1
- data/bin/console +1 -0
- data/bin/setup +5 -0
- data/docker-compose.yml +10 -0
- data/harvesting.gemspec +9 -6
- data/lib/harvesting.rb +20 -5
- data/lib/harvesting/client.rb +76 -15
- data/lib/harvesting/enumerable.rb +5 -2
- data/lib/harvesting/errors.rb +9 -0
- data/lib/harvesting/models/base.rb +93 -12
- data/lib/harvesting/models/client.rb +7 -1
- data/lib/harvesting/models/clients.rb +18 -0
- data/lib/harvesting/models/contact.rb +14 -1
- data/lib/harvesting/models/harvest_record.rb +18 -0
- data/lib/harvesting/models/harvest_record_collection.rb +43 -0
- data/lib/harvesting/models/invoice.rb +44 -0
- data/lib/harvesting/models/invoices.rb +17 -0
- data/lib/harvesting/models/line_item.rb +19 -0
- data/lib/harvesting/models/project.rb +28 -3
- data/lib/harvesting/models/project_task_assignment.rb +33 -0
- data/lib/harvesting/models/project_task_assignments.rb +18 -0
- data/lib/harvesting/models/project_user_assignment.rb +25 -0
- data/lib/harvesting/models/project_user_assignments.rb +19 -0
- data/lib/harvesting/models/projects.rb +6 -33
- data/lib/harvesting/models/task.rb +6 -3
- data/lib/harvesting/models/tasks.rb +6 -33
- data/lib/harvesting/models/time_entries.rb +6 -33
- data/lib/harvesting/models/time_entry.rb +22 -8
- data/lib/harvesting/models/user.rb +20 -6
- data/lib/harvesting/models/users.rb +18 -0
- data/lib/harvesting/version.rb +1 -1
- metadata +90 -21
- data/Gemfile.lock +0 -103
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
3
|
+
module Harvesting
|
4
|
+
module Models
|
5
|
+
class HarvestRecordCollection < Base
|
6
|
+
include Harvesting::Enumerable
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
attributed :per_page,
|
10
|
+
:total_pages,
|
11
|
+
:total_entries,
|
12
|
+
:next_page,
|
13
|
+
:previous_page,
|
14
|
+
:page,
|
15
|
+
:links
|
16
|
+
|
17
|
+
attr_reader :entries
|
18
|
+
|
19
|
+
def initialize(attrs, query_opts = {}, opts = {})
|
20
|
+
super(attrs, opts)
|
21
|
+
@query_opts = query_opts
|
22
|
+
@api_page = attrs
|
23
|
+
end
|
24
|
+
|
25
|
+
def page
|
26
|
+
@attributes['page']
|
27
|
+
end
|
28
|
+
|
29
|
+
def size
|
30
|
+
total_entries
|
31
|
+
end
|
32
|
+
|
33
|
+
def next_page_query_opts
|
34
|
+
@query_opts.merge(page: page + 1)
|
35
|
+
end
|
36
|
+
|
37
|
+
def fetch_next_page
|
38
|
+
raise NotImplementedError
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Harvesting
|
2
|
+
module Models
|
3
|
+
# An invoice record from your Harvest account.
|
4
|
+
#
|
5
|
+
# For more information: https://help.getharvest.com/api-v2/invoices-api/invoices/invoices/
|
6
|
+
class Invoice < HarvestRecord
|
7
|
+
attributed :id,
|
8
|
+
:client_key,
|
9
|
+
:number,
|
10
|
+
:purchase_order,
|
11
|
+
:amount,
|
12
|
+
:due_amount,
|
13
|
+
:tax,
|
14
|
+
:tax_amount,
|
15
|
+
:tax2,
|
16
|
+
:tax2_amount,
|
17
|
+
:discount,
|
18
|
+
:discount_amount,
|
19
|
+
:subject,
|
20
|
+
:notes,
|
21
|
+
:currency,
|
22
|
+
:state,
|
23
|
+
:period_start,
|
24
|
+
:period_end,
|
25
|
+
:issue_date,
|
26
|
+
:due_date,
|
27
|
+
:payment_term,
|
28
|
+
:sent_at,
|
29
|
+
:paid_at,
|
30
|
+
:paid_date,
|
31
|
+
:closed_at,
|
32
|
+
:created_at,
|
33
|
+
:updated_at
|
34
|
+
|
35
|
+
def line_items
|
36
|
+
@line_items ||= @attributes['line_items'].map { |line_item_attributes| LineItem.new line_item_attributes, { harvest_client: harvest_client } }
|
37
|
+
end
|
38
|
+
|
39
|
+
def path
|
40
|
+
@attributes['id'].nil? ? "invoices" : "invoices/#{@attributes['id']}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Harvesting
|
2
|
+
module Models
|
3
|
+
class Invoices < HarvestRecordCollection
|
4
|
+
def initialize(attrs, query_opts = {}, opts = {})
|
5
|
+
super(attrs.reject {|k,v| k == "invoices" }, query_opts, opts)
|
6
|
+
@entries = attrs["invoices"].map do |entry|
|
7
|
+
Invoice.new(entry, harvest_client: opts[:harvest_client])
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def fetch_next_page
|
12
|
+
@entries += harvest_client.invoices(next_page_query_opts).entries
|
13
|
+
@attributes['page'] = page + 1
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Harvesting
|
2
|
+
module Models
|
3
|
+
# A line item on an invoice from your Harvest account.
|
4
|
+
#
|
5
|
+
# For more information: https://help.getharvest.com/api-v2/invoices-api/invoices/invoices/
|
6
|
+
class LineItem < HarvestRecord
|
7
|
+
attributed :id,
|
8
|
+
:kind,
|
9
|
+
:description,
|
10
|
+
:quantity,
|
11
|
+
:unit_price,
|
12
|
+
:amount,
|
13
|
+
:taxed,
|
14
|
+
:taxed2
|
15
|
+
|
16
|
+
modeled project: Project
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -1,8 +1,10 @@
|
|
1
1
|
module Harvesting
|
2
2
|
module Models
|
3
|
-
|
3
|
+
# A project record from your Harvest account.
|
4
|
+
#
|
5
|
+
# For more information: https://help.getharvest.com/api-v2/projects-api/projects/projects/
|
6
|
+
class Project < HarvestRecord
|
4
7
|
attributed :id,
|
5
|
-
:client,
|
6
8
|
:name,
|
7
9
|
:code,
|
8
10
|
:is_active,
|
@@ -12,6 +14,7 @@ module Harvesting
|
|
12
14
|
:hourly_rate,
|
13
15
|
:budget,
|
14
16
|
:budget_by,
|
17
|
+
:budget_is_monthly,
|
15
18
|
:notify_when_over_budget,
|
16
19
|
:over_budget_notification_percentage,
|
17
20
|
:over_budget_notification_date,
|
@@ -25,8 +28,30 @@ module Harvesting
|
|
25
28
|
:created_at,
|
26
29
|
:updated_at
|
27
30
|
|
31
|
+
modeled client: Client
|
32
|
+
|
28
33
|
def path
|
29
|
-
id.nil? ? "projects" : "projects/#{id}"
|
34
|
+
@attributes['id'].nil? ? "projects" : "projects/#{@attributes['id']}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_hash
|
38
|
+
{ client_id: client.id }.merge(super)
|
39
|
+
end
|
40
|
+
|
41
|
+
def time_entries
|
42
|
+
harvest_client.time_entries(project_id: self.id)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Provides access to the user assignments that are associated with this
|
46
|
+
# project.
|
47
|
+
def user_assignments
|
48
|
+
harvest_client.user_assignments(project_id: self.id)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Provides access to the task assignments that are associated with this
|
52
|
+
# project.
|
53
|
+
def task_assignments
|
54
|
+
harvest_client.task_assignments(project_id: self.id)
|
30
55
|
end
|
31
56
|
end
|
32
57
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Harvesting
|
2
|
+
module Models
|
3
|
+
# A task assignment record from your Harvest account.
|
4
|
+
#
|
5
|
+
# For more information: https://help.getharvest.com/api-v2/projects-api/projects/task-assignments/
|
6
|
+
class ProjectTaskAssignment < HarvestRecord
|
7
|
+
attributed :id,
|
8
|
+
:is_active,
|
9
|
+
:billable,
|
10
|
+
:hourly_rate,
|
11
|
+
:budget,
|
12
|
+
:created_at,
|
13
|
+
:updated_at
|
14
|
+
|
15
|
+
modeled project: Project,
|
16
|
+
task: Task
|
17
|
+
|
18
|
+
def path
|
19
|
+
base_url = "projects/#{project.id}/task_assignments"
|
20
|
+
id.nil? ? base_url : "#{base_url}/#{id}"
|
21
|
+
end
|
22
|
+
|
23
|
+
# def project_id
|
24
|
+
# # TODO: handle case where project's id is part of json object
|
25
|
+
# @attributes["project_id"]
|
26
|
+
# end
|
27
|
+
|
28
|
+
def to_hash
|
29
|
+
{ project_id: project.id, task_id: task.id }.merge(super)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Harvesting
|
2
|
+
module Models
|
3
|
+
class ProjectTaskAssignments < HarvestRecordCollection
|
4
|
+
def initialize(attrs, query_opts = {}, opts = {})
|
5
|
+
super(attrs.reject {|k,v| k == "task_assignments" }, query_opts, opts)
|
6
|
+
@entries = attrs["task_assignments"].map do |entry|
|
7
|
+
ProjectTaskAssignment.new(entry, harvest_client: opts[:harvest_client])
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def fetch_next_page
|
12
|
+
@entries += harvest_client.task_assignments(next_page_query_opts).entries
|
13
|
+
@attributes['page'] = page + 1
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Harvesting
|
2
|
+
module Models
|
3
|
+
class ProjectUserAssignment < HarvestRecord
|
4
|
+
attributed :id,
|
5
|
+
:is_active,
|
6
|
+
:is_project_manager,
|
7
|
+
:hourly_rate,
|
8
|
+
:budget,
|
9
|
+
:created_at,
|
10
|
+
:updated_at
|
11
|
+
|
12
|
+
modeled project: Project,
|
13
|
+
user: User
|
14
|
+
|
15
|
+
def path
|
16
|
+
base_url = "projects/#{project.id}/user_assignments"
|
17
|
+
@attributes['id'].nil? ? base_url : "#{base_url}/#{@attributes['id']}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_hash
|
21
|
+
{ project_id: project.id, user_id: user.id }.merge(super)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Harvesting
|
2
|
+
module Models
|
3
|
+
class ProjectUserAssignments < HarvestRecordCollection
|
4
|
+
|
5
|
+
def initialize(attrs, query_opts = {}, opts = {})
|
6
|
+
super(attrs.reject {|k,v| k == "user_assignments" }, query_opts, opts)
|
7
|
+
@entries = attrs["user_assignments"].map do |entry|
|
8
|
+
ProjectUserAssignment.new(entry, harvest_client: opts[:harvest_client])
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def fetch_next_page
|
13
|
+
@entries += harvest_client.user_assignments(next_page_query_opts).entries
|
14
|
+
@attributes['page'] = page + 1
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -1,44 +1,17 @@
|
|
1
1
|
module Harvesting
|
2
2
|
module Models
|
3
|
-
class Projects <
|
4
|
-
include Harvesting::Enumerable
|
5
|
-
extend Forwardable
|
3
|
+
class Projects < HarvestRecordCollection
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
:total_entries,
|
10
|
-
:next_page,
|
11
|
-
:previous_page,
|
12
|
-
:page,
|
13
|
-
:links
|
14
|
-
|
15
|
-
attr_reader :entries
|
16
|
-
|
17
|
-
def initialize(attrs, opts = {})
|
18
|
-
super(attrs.reject {|k,v| k == "projects" }, opts)
|
19
|
-
@api_page = attrs
|
5
|
+
def initialize(attrs, query_opts = {}, opts = {})
|
6
|
+
super(attrs.reject {|k,v| k == "projects" }, query_opts, opts)
|
20
7
|
@entries = attrs["projects"].map do |entry|
|
21
|
-
Project.new(entry,
|
8
|
+
Project.new(entry, harvest_client: opts[:harvest_client])
|
22
9
|
end
|
23
10
|
end
|
24
11
|
|
25
|
-
# def each
|
26
|
-
# @entries.each_with_index do |time_entry, index|
|
27
|
-
# yield(time_entry)
|
28
|
-
# end
|
29
|
-
# end
|
30
|
-
def page
|
31
|
-
@attributes['page']
|
32
|
-
end
|
33
|
-
|
34
|
-
def size
|
35
|
-
total_entries
|
36
|
-
end
|
37
|
-
|
38
12
|
def fetch_next_page
|
39
|
-
|
40
|
-
@
|
41
|
-
@attributes['page'] = new_page
|
13
|
+
@entries += harvest_client.projects(next_page_query_opts).entries
|
14
|
+
@attributes['page'] = page + 1
|
42
15
|
end
|
43
16
|
end
|
44
17
|
end
|
@@ -1,9 +1,12 @@
|
|
1
1
|
module Harvesting
|
2
2
|
module Models
|
3
|
-
|
3
|
+
# A task record from your Harvest account.
|
4
|
+
#
|
5
|
+
# For more information: https://help.getharvest.com/api-v2/tasks-api/tasks/tasks/
|
6
|
+
class Task < HarvestRecord
|
4
7
|
attributed :id,
|
5
8
|
:name,
|
6
|
-
:billable_by_default,
|
9
|
+
:billable_by_default,
|
7
10
|
:default_hourly_rate,
|
8
11
|
:is_default,
|
9
12
|
:is_active,
|
@@ -11,7 +14,7 @@ module Harvesting
|
|
11
14
|
:updated_at
|
12
15
|
|
13
16
|
def path
|
14
|
-
id.nil? ? "tasks" : "tasks/#{id}"
|
17
|
+
@attributes['id'].nil? ? "tasks" : "tasks/#{@attributes['id']}"
|
15
18
|
end
|
16
19
|
end
|
17
20
|
end
|
@@ -1,44 +1,17 @@
|
|
1
1
|
module Harvesting
|
2
2
|
module Models
|
3
|
-
class Tasks <
|
4
|
-
include Harvesting::Enumerable
|
5
|
-
extend Forwardable
|
3
|
+
class Tasks < HarvestRecordCollection
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
:total_entries,
|
10
|
-
:next_page,
|
11
|
-
:previous_page,
|
12
|
-
:page,
|
13
|
-
:links
|
14
|
-
|
15
|
-
attr_reader :entries
|
16
|
-
|
17
|
-
def initialize(attrs, opts = {})
|
18
|
-
super(attrs.reject {|k,v| k == "tasks" }, opts)
|
19
|
-
@api_page = attrs
|
5
|
+
def initialize(attrs, query_opts = {}, opts = {})
|
6
|
+
super(attrs.reject {|k,v| k == "tasks" }, query_opts, opts)
|
20
7
|
@entries = attrs["tasks"].map do |entry|
|
21
|
-
Task.new(entry,
|
8
|
+
Task.new(entry, harvest_client: opts[:harvest_client])
|
22
9
|
end
|
23
10
|
end
|
24
11
|
|
25
|
-
# def each
|
26
|
-
# @entries.each_with_index do |time_entry, index|
|
27
|
-
# yield(time_entry)
|
28
|
-
# end
|
29
|
-
# end
|
30
|
-
def page
|
31
|
-
@attributes['page']
|
32
|
-
end
|
33
|
-
|
34
|
-
def size
|
35
|
-
total_entries
|
36
|
-
end
|
37
|
-
|
38
12
|
def fetch_next_page
|
39
|
-
|
40
|
-
@
|
41
|
-
@attributes['page'] = new_page
|
13
|
+
@entries += harvest_client.tasks(next_page_query_opts).entries
|
14
|
+
@attributes['page'] = page + 1
|
42
15
|
end
|
43
16
|
end
|
44
17
|
end
|
@@ -1,44 +1,17 @@
|
|
1
1
|
module Harvesting
|
2
2
|
module Models
|
3
|
-
class TimeEntries <
|
4
|
-
include Harvesting::Enumerable
|
5
|
-
extend Forwardable
|
3
|
+
class TimeEntries < HarvestRecordCollection
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
:total_entries,
|
10
|
-
:next_page,
|
11
|
-
:previous_page,
|
12
|
-
:page,
|
13
|
-
:links
|
14
|
-
|
15
|
-
attr_reader :entries
|
16
|
-
|
17
|
-
def initialize(attrs, opts = {})
|
18
|
-
super(attrs.reject {|k,v| k == "time_entries" }, opts)
|
19
|
-
@api_page = attrs
|
5
|
+
def initialize(attrs, query_opts = {}, opts = {})
|
6
|
+
super(attrs.reject {|k,v| k == "time_entries" }, query_opts, opts)
|
20
7
|
@entries = attrs["time_entries"].map do |entry|
|
21
|
-
TimeEntry.new(entry,
|
8
|
+
TimeEntry.new(entry, harvest_client: opts[:harvest_client])
|
22
9
|
end
|
23
10
|
end
|
24
11
|
|
25
|
-
# def each
|
26
|
-
# @entries.each_with_index do |time_entry, index|
|
27
|
-
# yield(time_entry)
|
28
|
-
# end
|
29
|
-
# end
|
30
|
-
def page
|
31
|
-
@attributes['page']
|
32
|
-
end
|
33
|
-
|
34
|
-
def size
|
35
|
-
total_entries
|
36
|
-
end
|
37
|
-
|
38
12
|
def fetch_next_page
|
39
|
-
|
40
|
-
@
|
41
|
-
@attributes['page'] = new_page
|
13
|
+
@entries += harvest_client.time_entries(next_page_query_opts).entries
|
14
|
+
@attributes['page'] = page + 1
|
42
15
|
end
|
43
16
|
end
|
44
17
|
end
|