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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.env.sample +10 -0
  3. data/.gitignore +13 -0
  4. data/.travis.yml +7 -2
  5. data/Dockerfile +6 -0
  6. data/README.md +173 -32
  7. data/RELEASE_NOTES.md +80 -0
  8. data/TODO.md +1 -1
  9. data/bin/console +1 -0
  10. data/bin/setup +5 -0
  11. data/docker-compose.yml +10 -0
  12. data/harvesting.gemspec +9 -6
  13. data/lib/harvesting.rb +20 -5
  14. data/lib/harvesting/client.rb +76 -15
  15. data/lib/harvesting/enumerable.rb +5 -2
  16. data/lib/harvesting/errors.rb +9 -0
  17. data/lib/harvesting/models/base.rb +93 -12
  18. data/lib/harvesting/models/client.rb +7 -1
  19. data/lib/harvesting/models/clients.rb +18 -0
  20. data/lib/harvesting/models/contact.rb +14 -1
  21. data/lib/harvesting/models/harvest_record.rb +18 -0
  22. data/lib/harvesting/models/harvest_record_collection.rb +43 -0
  23. data/lib/harvesting/models/invoice.rb +44 -0
  24. data/lib/harvesting/models/invoices.rb +17 -0
  25. data/lib/harvesting/models/line_item.rb +19 -0
  26. data/lib/harvesting/models/project.rb +28 -3
  27. data/lib/harvesting/models/project_task_assignment.rb +33 -0
  28. data/lib/harvesting/models/project_task_assignments.rb +18 -0
  29. data/lib/harvesting/models/project_user_assignment.rb +25 -0
  30. data/lib/harvesting/models/project_user_assignments.rb +19 -0
  31. data/lib/harvesting/models/projects.rb +6 -33
  32. data/lib/harvesting/models/task.rb +6 -3
  33. data/lib/harvesting/models/tasks.rb +6 -33
  34. data/lib/harvesting/models/time_entries.rb +6 -33
  35. data/lib/harvesting/models/time_entry.rb +22 -8
  36. data/lib/harvesting/models/user.rb +20 -6
  37. data/lib/harvesting/models/users.rb +18 -0
  38. data/lib/harvesting/version.rb +1 -1
  39. metadata +90 -21
  40. 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
- class Project < Base
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 < Base
4
- include Harvesting::Enumerable
5
- extend Forwardable
3
+ class Projects < HarvestRecordCollection
6
4
 
7
- attributed :per_page,
8
- :total_pages,
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, client: opts[:client])
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
- new_page = page + 1
40
- @entries += client.projects(page: new_page).entries
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
- class Task < Base
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 < Base
4
- include Harvesting::Enumerable
5
- extend Forwardable
3
+ class Tasks < HarvestRecordCollection
6
4
 
7
- attributed :per_page,
8
- :total_pages,
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, client: opts[:client])
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
- new_page = page + 1
40
- @entries += client.tasks(page: new_page).entries
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 < Base
4
- include Harvesting::Enumerable
5
- extend Forwardable
3
+ class TimeEntries < HarvestRecordCollection
6
4
 
7
- attributed :per_page,
8
- :total_pages,
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, client: opts[:client])
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
- new_page = page + 1
40
- @entries += @client.time_entries(page: new_page).entries
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