harvest-ruby-v2 0.2.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 +7 -0
- data/.gitignore +14 -0
- data/.rspec +3 -0
- data/.rubocop.yml +117 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.rst +0 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/EXAMPLE.conf +4 -0
- data/Gemfile +39 -0
- data/LICENSE.txt +21 -0
- data/Makefile +9 -0
- data/README.md +44 -0
- data/Rakefile +34 -0
- data/bin/console +53 -0
- data/bin/setup +8 -0
- data/harvest-ruby-v2.gemspec +31 -0
- data/lib/harvest.rb +115 -0
- data/lib/harvest/client.rb +0 -0
- data/lib/harvest/config.rb +21 -0
- data/lib/harvest/creates.rb +46 -0
- data/lib/harvest/discovers.rb +58 -0
- data/lib/harvest/exceptions.rb +11 -0
- data/lib/harvest/finders.rb +17 -0
- data/lib/harvest/http/client.rb +0 -0
- data/lib/harvest/httpclient.rb +178 -0
- data/lib/harvest/resourcefactory.rb +220 -0
- data/lib/harvest/resources.rb +15 -0
- data/lib/harvest/resources/client.rb +34 -0
- data/lib/harvest/resources/company.rb +62 -0
- data/lib/harvest/resources/estimates.rb +173 -0
- data/lib/harvest/resources/expenses.rb +90 -0
- data/lib/harvest/resources/invoices.rb +262 -0
- data/lib/harvest/resources/message.rb +16 -0
- data/lib/harvest/resources/project.rb +84 -0
- data/lib/harvest/resources/project_assignment.rb +46 -0
- data/lib/harvest/resources/task.rb +35 -0
- data/lib/harvest/resources/task_assignment.rb +38 -0
- data/lib/harvest/resources/timeentry.rb +105 -0
- data/lib/harvest/resources/user.rb +75 -0
- data/lib/harvest/resources/user_assignment.rb +43 -0
- data/lib/harvest/version.rb +5 -0
- metadata +86 -0
@@ -0,0 +1,220 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pry'
|
4
|
+
require 'harvest/resources'
|
5
|
+
|
6
|
+
module Harvest
|
7
|
+
# Conversion for hash to Struct including nested items.
|
8
|
+
# TODO: Refactor for figuring out what Struct should be used
|
9
|
+
class NoValidStruct < ArgumentError
|
10
|
+
end
|
11
|
+
|
12
|
+
class ResourceFactory
|
13
|
+
def resource(klass, data)
|
14
|
+
data ||= {}
|
15
|
+
send(klass.to_sym, data)
|
16
|
+
end
|
17
|
+
|
18
|
+
def message_recipient(data)
|
19
|
+
data ||= {}
|
20
|
+
Struct::MessageRecipient.new(data)
|
21
|
+
end
|
22
|
+
|
23
|
+
def company(data)
|
24
|
+
data ||= {}
|
25
|
+
Struct::Company.new(data)
|
26
|
+
end
|
27
|
+
|
28
|
+
def estimate(data)
|
29
|
+
data ||= {}
|
30
|
+
convert_estimate_line_items(convert_client(Struct::Estimate.new(data)))
|
31
|
+
end
|
32
|
+
|
33
|
+
def estimate_line_item(data)
|
34
|
+
data ||= {}
|
35
|
+
Struct::EstimateLineItem.new(data)
|
36
|
+
end
|
37
|
+
|
38
|
+
def estimate_message(data)
|
39
|
+
data ||= {}
|
40
|
+
Struct::EstimateMessage.new(data)
|
41
|
+
end
|
42
|
+
|
43
|
+
def estimate_item_category(data)
|
44
|
+
data ||= {}
|
45
|
+
Struct::EstimateItemCategory.new(data)
|
46
|
+
end
|
47
|
+
|
48
|
+
def expense_category(data)
|
49
|
+
data ||= {}
|
50
|
+
Struct::ExpenseCategory.new(data)
|
51
|
+
end
|
52
|
+
|
53
|
+
def expense(data)
|
54
|
+
data ||= {}
|
55
|
+
Struct::Expense.new(data)
|
56
|
+
end
|
57
|
+
|
58
|
+
def invoice(data)
|
59
|
+
data ||= {}
|
60
|
+
Struct::Invoice.new(data)
|
61
|
+
end
|
62
|
+
|
63
|
+
def invoice_line_item(data)
|
64
|
+
data ||= {}
|
65
|
+
Struct::InvoiceLineItem.new(data)
|
66
|
+
end
|
67
|
+
|
68
|
+
def invoice_message(data)
|
69
|
+
data ||= {}
|
70
|
+
Struct::InvoiceMessage.new(data)
|
71
|
+
end
|
72
|
+
|
73
|
+
def invoice_payment(data)
|
74
|
+
data ||= {}
|
75
|
+
Struct::InvoicePayment.new(data)
|
76
|
+
end
|
77
|
+
|
78
|
+
def invoice_item_category(data)
|
79
|
+
data ||= {}
|
80
|
+
Struct::InvoiceItemCategory.new(data)
|
81
|
+
end
|
82
|
+
|
83
|
+
def project_assignment(data)
|
84
|
+
data ||= {}
|
85
|
+
unless data.nil?
|
86
|
+
convert_dates(
|
87
|
+
convert_project_client(
|
88
|
+
convert_task_assignments(
|
89
|
+
Struct::ProjectAssignment.new(data)
|
90
|
+
)
|
91
|
+
)
|
92
|
+
)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def project(data)
|
97
|
+
data ||= {}
|
98
|
+
convert_dates(Struct::Project.new(data)) unless data.nil?
|
99
|
+
end
|
100
|
+
|
101
|
+
def client(data)
|
102
|
+
data ||= {}
|
103
|
+
convert_dates(Struct::ResourceClient.new(data)) unless data.nil?
|
104
|
+
end
|
105
|
+
|
106
|
+
def task_assignment(data)
|
107
|
+
data ||= {}
|
108
|
+
convert_dates(convert_task(Struct::TaskAssignment.new(data))) unless data.nil?
|
109
|
+
end
|
110
|
+
|
111
|
+
def task(data)
|
112
|
+
data ||= {}
|
113
|
+
convert_dates(Struct::Task.new(data)) unless data.nil?
|
114
|
+
end
|
115
|
+
|
116
|
+
def user(data)
|
117
|
+
data ||= {}
|
118
|
+
convert_dates(Struct::User.new(data)) unless data.nil?
|
119
|
+
end
|
120
|
+
|
121
|
+
def user_assignment(data)
|
122
|
+
data ||= {}
|
123
|
+
convert_dates(Struct::UserAssignment.new(data)) unless data.nil?
|
124
|
+
end
|
125
|
+
|
126
|
+
def time_entry_external_reference(data)
|
127
|
+
data ||= {}
|
128
|
+
Struct::TimeEntryExternalReference.new(data)
|
129
|
+
end
|
130
|
+
|
131
|
+
def time_entry(data)
|
132
|
+
data ||= {}
|
133
|
+
convert_dates(
|
134
|
+
convert_project_client(
|
135
|
+
convert_task_assignment(
|
136
|
+
convert_task(
|
137
|
+
convert_user_assignment(
|
138
|
+
convert_user(
|
139
|
+
convert_external_reference(
|
140
|
+
Struct::TimeEntry.new(data)
|
141
|
+
)
|
142
|
+
)
|
143
|
+
)
|
144
|
+
)
|
145
|
+
)
|
146
|
+
)
|
147
|
+
)
|
148
|
+
end
|
149
|
+
|
150
|
+
private
|
151
|
+
|
152
|
+
def convert_estimate_line_items(data)
|
153
|
+
unless data.line_items.nil?
|
154
|
+
data.line_items = data.line_items.map { |ta| estimate_line_item(ta) }
|
155
|
+
end
|
156
|
+
data
|
157
|
+
end
|
158
|
+
|
159
|
+
def convert_external_reference(data)
|
160
|
+
data.external_reference = time_entry_external_reference(data.external_reference)
|
161
|
+
data
|
162
|
+
end
|
163
|
+
|
164
|
+
def convert_user(data)
|
165
|
+
data.user ||= {}
|
166
|
+
data.user = user(data.user) unless data.nil?
|
167
|
+
data
|
168
|
+
end
|
169
|
+
|
170
|
+
def convert_user_assignment(data)
|
171
|
+
data.user_assignment ||= {}
|
172
|
+
data.user_assignment = user_assignment(data.user_assignment) unless data.nil?
|
173
|
+
data
|
174
|
+
end
|
175
|
+
|
176
|
+
def convert_task_assignment(data)
|
177
|
+
data.task_assignment ||= {}
|
178
|
+
|
179
|
+
data.task_assignment = task_assignment(data.task_assignment) unless data.task_assignment.nil?
|
180
|
+
data
|
181
|
+
end
|
182
|
+
|
183
|
+
def convert_task_assignments(data)
|
184
|
+
unless data.task_assignments.nil?
|
185
|
+
data.task_assignments = data.task_assignments.map { |ta| task_assignment(ta) }
|
186
|
+
end
|
187
|
+
data
|
188
|
+
end
|
189
|
+
|
190
|
+
def convert_task(data)
|
191
|
+
data.task ||= {}
|
192
|
+
data.task = task(data.task) unless data.task.nil?
|
193
|
+
data
|
194
|
+
end
|
195
|
+
|
196
|
+
# @param data [Struct] passed a struct which contains an attribute client
|
197
|
+
def convert_client(data)
|
198
|
+
data.client = client(data.client)
|
199
|
+
data
|
200
|
+
end
|
201
|
+
|
202
|
+
def convert_project(data)
|
203
|
+
data.project = project(data.project)
|
204
|
+
data
|
205
|
+
end
|
206
|
+
|
207
|
+
def convert_project_client(data)
|
208
|
+
convert_project(convert_client(data))
|
209
|
+
end
|
210
|
+
|
211
|
+
def convert_dates(data)
|
212
|
+
%i[created_at updated_at sent_at accepted_at declined_at].each do |key|
|
213
|
+
if data.members.include?(key) && !data.method(key).call.nil?
|
214
|
+
data.method("#{key}=").call(DateTime.strptime(data.method(key).call))
|
215
|
+
end
|
216
|
+
end
|
217
|
+
data
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'harvest/resources/message'
|
4
|
+
require 'harvest/resources/company'
|
5
|
+
require 'harvest/resources/user'
|
6
|
+
require 'harvest/resources/task_assignment'
|
7
|
+
require 'harvest/resources/user_assignment'
|
8
|
+
require 'harvest/resources/timeentry'
|
9
|
+
require 'harvest/resources/estimates'
|
10
|
+
require 'harvest/resources/client'
|
11
|
+
require 'harvest/resources/expenses'
|
12
|
+
require 'harvest/resources/task'
|
13
|
+
require 'harvest/resources/project'
|
14
|
+
require 'harvest/resources/project_assignment'
|
15
|
+
require 'harvest/resources/invoices'
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Harvest
|
4
|
+
module Resources
|
5
|
+
# @param id [Integer]
|
6
|
+
# Unique ID for the client.
|
7
|
+
# @param name [String]
|
8
|
+
# A textual description of the client.
|
9
|
+
# @param is_active [Boolean]
|
10
|
+
# Whether the client is active or archived.
|
11
|
+
# @param address [String]
|
12
|
+
# The physical address for the client.
|
13
|
+
# @param statement_key [String]
|
14
|
+
# Used to build a URL to your client's invoice dashboard:
|
15
|
+
# @param currency [String]
|
16
|
+
# The currency code associated with this client.
|
17
|
+
# @param created_at [DateTime]
|
18
|
+
# Date and time the client was created.
|
19
|
+
# @param updated_at [DateTime]
|
20
|
+
# Date and time the client was last updated.
|
21
|
+
ResourceClient = Struct.new(
|
22
|
+
'ResourceClient',
|
23
|
+
:id,
|
24
|
+
:name,
|
25
|
+
:is_active,
|
26
|
+
:address,
|
27
|
+
:statement_key,
|
28
|
+
:currency,
|
29
|
+
:created_at,
|
30
|
+
:updated_at,
|
31
|
+
keyword_init: true
|
32
|
+
)
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Harvest
|
4
|
+
module Resources
|
5
|
+
# @param base_uri [String]
|
6
|
+
# The Harvest URL for the company.
|
7
|
+
# @param full_domain [String]
|
8
|
+
# The Harvest domain for the company.
|
9
|
+
# @param name [String]
|
10
|
+
# The name of the company.
|
11
|
+
# @param is_active [Boolean]
|
12
|
+
# Whether the company is active or archived.
|
13
|
+
# @param week_start_day [String]
|
14
|
+
# The week day used as the start of the week. Returns one of: Saturday, Sunday, or Monday.
|
15
|
+
# @param wants_timestamp_timers [Boolean]
|
16
|
+
# Whether time is tracked via duration or start and end times.
|
17
|
+
# @param time_format [String]
|
18
|
+
# The format used to display time in Harvest. Returns either decimal or hours_minutes.
|
19
|
+
# @param plan_type [String]
|
20
|
+
# The type of plan the company is on. Examples: trial, free, or simple-v4
|
21
|
+
# @param clock [String]
|
22
|
+
# Used to represent whether the company is using a 12-hour or 24-hour clock.
|
23
|
+
# Returns either 12h or 24h.
|
24
|
+
# @param decimal_symbol [String]
|
25
|
+
# Symbol used when formatting decimals.
|
26
|
+
# @param thousands_separator [String]
|
27
|
+
# Separator used when formatting numbers.
|
28
|
+
# @param color_scheme [String]
|
29
|
+
# The color scheme being used in the Harvest web client.
|
30
|
+
# @param weekly_capacity [Integer]
|
31
|
+
# The weekly capacity in seconds.
|
32
|
+
# @param expense_feature [Boolean]
|
33
|
+
# Whether the expense module is enabled.
|
34
|
+
# @param invoice_feature [Boolean]
|
35
|
+
# Whether the invoice module is enabled.
|
36
|
+
# @param estimate_feature [Boolean]
|
37
|
+
# Whether the estimate module is enabled.
|
38
|
+
# @param approval_feature [Boolean]
|
39
|
+
# Whether the approval module is enabled.
|
40
|
+
Company = Struct.new(
|
41
|
+
'Company',
|
42
|
+
:base_uri,
|
43
|
+
:full_domain,
|
44
|
+
:name,
|
45
|
+
:is_active,
|
46
|
+
:week_start_day,
|
47
|
+
:wants_timestamp_timers,
|
48
|
+
:time_format,
|
49
|
+
:plan_type,
|
50
|
+
:clock,
|
51
|
+
:decimal_symbol,
|
52
|
+
:thousands_separator,
|
53
|
+
:color_scheme,
|
54
|
+
:weekly_capacity,
|
55
|
+
:expense_feature,
|
56
|
+
:invoice_feature,
|
57
|
+
:estimate_feature,
|
58
|
+
:approval_feature,
|
59
|
+
keyword_init: true
|
60
|
+
)
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Harvest
|
4
|
+
module Resources
|
5
|
+
# @param id [Integer]
|
6
|
+
# Unique ID for the estimate.
|
7
|
+
# @param client [Struct]
|
8
|
+
# An object containing estimate's client id and name.
|
9
|
+
# @param line_items [List]
|
10
|
+
# Array of estimate line items.
|
11
|
+
# @param creator [Struct]
|
12
|
+
# An object containing the id and name of the person that created the estimate.
|
13
|
+
# @param client_key [String]
|
14
|
+
# Used to build a URL to the public web invoice for your client:
|
15
|
+
# @param number [String]
|
16
|
+
# If no value is set, the number will be automatically generated.
|
17
|
+
# @param purchase_order [String]
|
18
|
+
# The purchase order number.
|
19
|
+
# @param amount [decimal]
|
20
|
+
# The total amount for the estimate, including any discounts and taxes.
|
21
|
+
# @param tax [decimal]
|
22
|
+
# This percentage is applied to the subtotal, including line items and
|
23
|
+
# discounts.
|
24
|
+
# @param tax_amount [decimal]
|
25
|
+
# The first amount of tax included, calculated from tax. If no tax is
|
26
|
+
# defined, this value will be null.
|
27
|
+
# @param tax2 [decimal]
|
28
|
+
# This percentage is applied to the subtotal, including line items and
|
29
|
+
# discounts.
|
30
|
+
# @param tax2_amount [decimal]
|
31
|
+
# The amount calculated from tax2.
|
32
|
+
# @param discount [decimal]
|
33
|
+
# This percentage is subtracted from the subtotal.
|
34
|
+
# @param discount_amount [decimal]
|
35
|
+
# The amount calcuated from discount.
|
36
|
+
# @param subject [String]
|
37
|
+
# The estimate subject.
|
38
|
+
# @param notes [String]
|
39
|
+
# Any additional notes included on the estimate.
|
40
|
+
# @param currency [String]
|
41
|
+
# The currency code associated with this estimate.
|
42
|
+
# @param state [String]
|
43
|
+
# The current state of the estimate: draft, sent, accepted, or declined.
|
44
|
+
# @param issue_date [Date]
|
45
|
+
# Date the estimate was issued.
|
46
|
+
# @param sent_at [DateTime]
|
47
|
+
# Date and time the estimate was sent.
|
48
|
+
# @param accepted_at [DateTime]
|
49
|
+
# Date and time the estimate was accepted.
|
50
|
+
# @param declined_at [DateTime]
|
51
|
+
# Date and time the estimate was declined.
|
52
|
+
# @param created_at [DateTime]
|
53
|
+
# Date and time the estimate was created.
|
54
|
+
# @param updated_at [DateTime]
|
55
|
+
# Date and time the estimate was last updated.
|
56
|
+
Estimate = Struct.new(
|
57
|
+
'Estimate',
|
58
|
+
:id,
|
59
|
+
:client,
|
60
|
+
:line_items,
|
61
|
+
:creator,
|
62
|
+
:client_key,
|
63
|
+
:number,
|
64
|
+
:purchase_order,
|
65
|
+
:amount,
|
66
|
+
:tax,
|
67
|
+
:tax_amount,
|
68
|
+
:tax2,
|
69
|
+
:tax2_amount,
|
70
|
+
:discount,
|
71
|
+
:discount_amount,
|
72
|
+
:subject,
|
73
|
+
:notes,
|
74
|
+
:currency,
|
75
|
+
:state,
|
76
|
+
:issue_date,
|
77
|
+
:sent_at,
|
78
|
+
:accepted_at,
|
79
|
+
:declined_at,
|
80
|
+
:created_at,
|
81
|
+
:updated_at,
|
82
|
+
keyword_init: true
|
83
|
+
)
|
84
|
+
|
85
|
+
# @param id [Integer]
|
86
|
+
# Unique ID for the line item.
|
87
|
+
# @param kind [String]
|
88
|
+
# The name of an estimate item category.
|
89
|
+
# @param description [String]
|
90
|
+
# Text description of the line item.
|
91
|
+
# @param quantity [Integer]
|
92
|
+
# The unit quantity of the item.
|
93
|
+
# @param unit_price [decimal]
|
94
|
+
# The individual price per unit.
|
95
|
+
# @param amount [decimal]
|
96
|
+
# The line item subtotal (quantity * unit_price).
|
97
|
+
# @param taxed [Boolean]
|
98
|
+
# Whether the estimate's tax percentage applies to this line item.
|
99
|
+
# @param taxed2 [Boolean]
|
100
|
+
# Whether the estimate's tax2 percentage applies to this line item.
|
101
|
+
EstimateLineItem = Struct.new(
|
102
|
+
'EstimateLineItem',
|
103
|
+
:id,
|
104
|
+
:kind,
|
105
|
+
:description,
|
106
|
+
:quantity,
|
107
|
+
:unit_price,
|
108
|
+
:amount,
|
109
|
+
:taxed,
|
110
|
+
:taxed2,
|
111
|
+
keyword_init: true
|
112
|
+
)
|
113
|
+
|
114
|
+
# @param id [Integer]
|
115
|
+
# Unique ID for the message.
|
116
|
+
# @param sent_by [String]
|
117
|
+
# Name of the user that created the message.
|
118
|
+
# @param sent_by_email [String]
|
119
|
+
# Email of the user that created the message.
|
120
|
+
# @param sent_from [String]
|
121
|
+
# Name of the user that the message was sent from.
|
122
|
+
# @param sent_from_email [String]
|
123
|
+
# Email of the user that message was sent from.
|
124
|
+
# @param recipients [List]
|
125
|
+
# Array of estimate message recipients.
|
126
|
+
# @param subject [String]
|
127
|
+
# The message subject.
|
128
|
+
# @param body [String]
|
129
|
+
# The message body.
|
130
|
+
# @param send_me_a_copy [Boolean]
|
131
|
+
# Whether to email a copy of the message to the current user.
|
132
|
+
# @param event_type [String]
|
133
|
+
# The type of estimate event that occurred with the message: send, accept,
|
134
|
+
# decline, re-open, view, or invoice.
|
135
|
+
# @param created_at [DateTime]
|
136
|
+
# Date and time the message was created.
|
137
|
+
# @param updated_at [DateTime]
|
138
|
+
# Date and time the message was last updated.
|
139
|
+
EstimateMessage = Struct.new(
|
140
|
+
'EstimateMessage',
|
141
|
+
:id,
|
142
|
+
:sent_by,
|
143
|
+
:sent_by_email,
|
144
|
+
:sent_from,
|
145
|
+
:sent_from_email,
|
146
|
+
:recipients,
|
147
|
+
:subject,
|
148
|
+
:body,
|
149
|
+
:send_me_a_copy,
|
150
|
+
:event_type,
|
151
|
+
:created_at,
|
152
|
+
:updated_at,
|
153
|
+
keyword_init: true
|
154
|
+
)
|
155
|
+
|
156
|
+
# @param id [Integer]
|
157
|
+
# Unique ID for the estimate item category.
|
158
|
+
# @param name [String]
|
159
|
+
# The name of the estimate item category.
|
160
|
+
# @param created_at [DateTime]
|
161
|
+
# Date and time the estimate item category was created.
|
162
|
+
# @param updated_at [DateTime]
|
163
|
+
# Date and time the estimate item category was last updated.
|
164
|
+
EstimateItemCategory = Struct.new(
|
165
|
+
'EstimateItemCategory',
|
166
|
+
:id,
|
167
|
+
:name,
|
168
|
+
:created_at,
|
169
|
+
:updated_at,
|
170
|
+
keyword_init: true
|
171
|
+
)
|
172
|
+
end
|
173
|
+
end
|