harvested 0.3.3 → 0.4.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.
- data/Gemfile +15 -0
- data/HISTORY +11 -0
- data/README.md +14 -6
- data/Rakefile +13 -31
- data/TODO +2 -0
- data/VERSION +1 -1
- data/examples/user_assignments.rb +1 -1
- data/harvested.gemspec +114 -126
- data/lib/ext/array.rb +52 -0
- data/lib/ext/date.rb +9 -0
- data/lib/ext/hash.rb +17 -0
- data/lib/ext/time.rb +5 -0
- data/lib/harvest/api/account.rb +8 -1
- data/lib/harvest/api/base.rb +32 -10
- data/lib/harvest/api/contacts.rb +1 -1
- data/lib/harvest/api/expenses.rb +3 -4
- data/lib/harvest/api/invoice_categories.rb +26 -0
- data/lib/harvest/api/invoices.rb +16 -0
- data/lib/harvest/api/projects.rb +2 -10
- data/lib/harvest/api/reports.rb +13 -16
- data/lib/harvest/api/task_assignments.rb +8 -6
- data/lib/harvest/api/tasks.rb +1 -1
- data/lib/harvest/api/time.rb +13 -13
- data/lib/harvest/api/user_assignments.rb +7 -5
- data/lib/harvest/base.rb +9 -1
- data/lib/harvest/behavior/activatable.rb +2 -2
- data/lib/harvest/behavior/crud.rb +15 -13
- data/lib/harvest/client.rb +18 -13
- data/lib/harvest/contact.rb +13 -11
- data/lib/harvest/errors.rb +6 -4
- data/lib/harvest/expense.rb +40 -14
- data/lib/harvest/expense_category.rb +10 -9
- data/lib/harvest/hardy_client.rb +1 -1
- data/lib/harvest/invoice.rb +103 -0
- data/lib/harvest/invoice_category.rb +18 -0
- data/lib/harvest/line_item.rb +12 -0
- data/lib/harvest/model.rb +120 -0
- data/lib/harvest/project.rb +55 -26
- data/lib/harvest/rate_limit_status.rb +9 -8
- data/lib/harvest/task.rb +17 -14
- data/lib/harvest/task_assignment.rb +27 -22
- data/lib/harvest/time_entry.rb +32 -30
- data/lib/harvest/user.rb +46 -22
- data/lib/harvest/user_assignment.rb +24 -17
- data/lib/harvested.rb +12 -5
- data/spec/functional/account_spec.rb +17 -0
- data/spec/functional/clients_spec.rb +58 -0
- data/spec/functional/errors_spec.rb +22 -0
- data/spec/functional/expenses_spec.rb +84 -0
- data/spec/functional/hardy_client_spec.rb +33 -0
- data/spec/functional/invoice_spec.rb +67 -0
- data/spec/functional/project_spec.rb +50 -0
- data/spec/functional/reporting_spec.rb +80 -0
- data/spec/functional/tasks_spec.rb +88 -0
- data/spec/functional/time_tracking_spec.rb +53 -0
- data/spec/functional/users_spec.rb +102 -0
- data/spec/harvest/base_spec.rb +1 -1
- data/spec/harvest/credentials_spec.rb +1 -1
- data/spec/harvest/expense_category_spec.rb +5 -0
- data/spec/harvest/expense_spec.rb +8 -5
- data/spec/harvest/invoice_spec.rb +47 -0
- data/spec/harvest/project_spec.rb +11 -0
- data/spec/harvest/task_assignment_spec.rb +4 -4
- data/spec/harvest/task_spec.rb +7 -0
- data/spec/harvest/time_entry_spec.rb +11 -10
- data/spec/harvest/user_assignment_spec.rb +3 -3
- data/spec/harvest/user_spec.rb +3 -1
- data/spec/spec_helper.rb +37 -6
- data/{features → spec}/support/harvest_credentials.example.yml +0 -1
- data/spec/support/harvested_helpers.rb +44 -0
- data/spec/support/json_examples.rb +11 -0
- data/spec/test_rubies +5 -0
- metadata +109 -85
- data/.gitignore +0 -28
- data/features/account.feature +0 -7
- data/features/client_contacts.feature +0 -23
- data/features/clients.feature +0 -29
- data/features/errors.feature +0 -25
- data/features/expense_categories.feature +0 -21
- data/features/expenses.feature +0 -55
- data/features/hardy_client.feature +0 -40
- data/features/projects.feature +0 -39
- data/features/reporting.feature +0 -72
- data/features/step_definitions/account_steps.rb +0 -7
- data/features/step_definitions/assignment_steps.rb +0 -100
- data/features/step_definitions/contact_steps.rb +0 -11
- data/features/step_definitions/debug_steps.rb +0 -3
- data/features/step_definitions/error_steps.rb +0 -113
- data/features/step_definitions/expenses_steps.rb +0 -46
- data/features/step_definitions/harvest_steps.rb +0 -8
- data/features/step_definitions/model_steps.rb +0 -90
- data/features/step_definitions/people_steps.rb +0 -4
- data/features/step_definitions/report_steps.rb +0 -91
- data/features/step_definitions/time_entry_steps.rb +0 -40
- data/features/support/env.rb +0 -37
- data/features/support/error_helpers.rb +0 -18
- data/features/support/fixtures/empty_clients.xml +0 -2
- data/features/support/fixtures/over_limit.xml +0 -8
- data/features/support/fixtures/receipt.png +0 -0
- data/features/support/fixtures/under_limit.xml +0 -8
- data/features/support/harvest_helpers.rb +0 -11
- data/features/support/inflections.rb +0 -9
- data/features/task_assignment.feature +0 -69
- data/features/tasks.feature +0 -25
- data/features/time_tracking.feature +0 -29
- data/features/user_assignments.feature +0 -33
- data/features/users.feature +0 -55
- data/lib/harvest/base_model.rb +0 -73
- data/spec/spec.default.opts +0 -1
data/lib/harvested.rb
CHANGED
@@ -1,8 +1,15 @@
|
|
1
|
-
require 'happymapper'
|
2
1
|
require 'httparty'
|
3
2
|
require 'base64'
|
4
|
-
require 'builder'
|
5
3
|
require 'delegate'
|
4
|
+
require 'hashie'
|
5
|
+
require 'json'
|
6
|
+
require 'time'
|
7
|
+
require 'csv'
|
8
|
+
|
9
|
+
require 'ext/array'
|
10
|
+
require 'ext/hash'
|
11
|
+
require 'ext/date'
|
12
|
+
require 'ext/time'
|
6
13
|
|
7
14
|
require 'harvest/credentials'
|
8
15
|
require 'harvest/errors'
|
@@ -12,11 +19,11 @@ require 'harvest/timezones'
|
|
12
19
|
require 'harvest/base'
|
13
20
|
|
14
21
|
%w(crud activatable).each {|a| require "harvest/behavior/#{a}"}
|
15
|
-
%w(
|
16
|
-
%w(base account clients contacts projects tasks users task_assignments user_assignments expense_categories expenses time reports).each {|a| require "harvest/api/#{a}"}
|
22
|
+
%w(model client contact project task user rate_limit_status task_assignment user_assignment expense_category expense time_entry invoice_category line_item invoice).each {|a| require "harvest/#{a}"}
|
23
|
+
%w(base account clients contacts projects tasks users task_assignments user_assignments expense_categories expenses time reports invoice_categories invoices).each {|a| require "harvest/api/#{a}"}
|
17
24
|
|
18
25
|
module Harvest
|
19
|
-
VERSION =
|
26
|
+
VERSION = File.read(File.expand_path(File.join(File.dirname(__FILE__), '..', 'VERSION'))).strip
|
20
27
|
|
21
28
|
class << self
|
22
29
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'account information' do
|
4
|
+
it 'returns the rate limit when requested' do
|
5
|
+
cassette('account1') do
|
6
|
+
status = harvest.account.rate_limit_status
|
7
|
+
status.max_calls.should == 100
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'returns the result of whoami' do
|
12
|
+
cassette('account2') do
|
13
|
+
user = harvest.account.who_am_i
|
14
|
+
user.email.should == credentials["username"]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'harvest clients' do
|
4
|
+
it 'allows adding, updating and removing clients' do
|
5
|
+
cassette("client") do
|
6
|
+
client = harvest.clients.create(
|
7
|
+
"name" => "Joe's Steam Cleaning",
|
8
|
+
"details" => "Building API Widgets across the country"
|
9
|
+
)
|
10
|
+
client.name.should == "Joe's Steam Cleaning"
|
11
|
+
|
12
|
+
client.name = "Joe and Frank's Steam Cleaning"
|
13
|
+
client = harvest.clients.update(client)
|
14
|
+
client.name.should == "Joe and Frank's Steam Cleaning"
|
15
|
+
|
16
|
+
harvest.clients.delete(client)
|
17
|
+
harvest.clients.all.select {|p| p.name == "Joe and Frank's Steam Cleaning"}.should == []
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'allows activating and deactivating clients' do
|
22
|
+
cassette("client2") do
|
23
|
+
client = harvest.clients.create(
|
24
|
+
"name" => "Joe's Steam Cleaning",
|
25
|
+
"details" => "Building API Widgets across the country"
|
26
|
+
)
|
27
|
+
client.should be_active
|
28
|
+
|
29
|
+
client = harvest.clients.deactivate(client)
|
30
|
+
client.should_not be_active
|
31
|
+
|
32
|
+
client = harvest.clients.activate(client)
|
33
|
+
client.should be_active
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "contacts" do
|
38
|
+
it "allows adding, updating, and removing contacts" do
|
39
|
+
cassette("client3") do
|
40
|
+
client = harvest.clients.create(
|
41
|
+
"name" => "Joe's Steam Cleaning",
|
42
|
+
"details" => "Building API Widgets across the country"
|
43
|
+
)
|
44
|
+
contact = harvest.contacts.create(
|
45
|
+
"client_id" => client.id,
|
46
|
+
"email" => "jane@doe.com",
|
47
|
+
"first_name" => "Jane",
|
48
|
+
"last_name" => "Doe"
|
49
|
+
)
|
50
|
+
contact.client_id.should == client.id
|
51
|
+
contact.email.should == "jane@doe.com"
|
52
|
+
|
53
|
+
harvest.contacts.delete(contact)
|
54
|
+
harvest.contacts.all.select {|e| e.email == "jane@doe.com" }.should == []
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'harvest errors' do
|
4
|
+
before { WebMock.disable_net_connect! }
|
5
|
+
|
6
|
+
it "wraps errors" do
|
7
|
+
stub_request(:get, /\/clients/).to_return({:status => ['400', 'Bad Request']}, {:body => "[]", :status => 200})
|
8
|
+
expect { harvest.clients.all }.to raise_error(Harvest::BadRequest)
|
9
|
+
|
10
|
+
stub_request(:get, /\/clients/).to_return({:status => ['404', 'Not Found']}, {:body => "[]", :status => 200})
|
11
|
+
expect { harvest.clients.all }.to raise_error(Harvest::NotFound)
|
12
|
+
|
13
|
+
stub_request(:get, /\/clients/).to_return({:status => ['500', 'Server Error']}, {:body => "[]", :status => 200})
|
14
|
+
expect { harvest.clients.all }.to raise_error(Harvest::ServerError)
|
15
|
+
|
16
|
+
stub_request(:get, /\/clients/).to_return({:status => ['502', 'Bad Gateway']}, {:body => "[]", :status => 200})
|
17
|
+
expect { harvest.clients.all }.to raise_error(Harvest::Unavailable)
|
18
|
+
|
19
|
+
stub_request(:get, /\/clients/).to_return({:status => ['503', 'Rate Limited']}, {:body => "[]", :status => 200})
|
20
|
+
expect { harvest.clients.all }.to raise_error(Harvest::RateLimited)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'harvest expenses' do
|
4
|
+
context 'categories' do
|
5
|
+
it 'allows adding, updating and removing categories' do
|
6
|
+
cassette("expense_category1") do
|
7
|
+
category = harvest.expense_categories.create(
|
8
|
+
"name" => "Mileage",
|
9
|
+
"unit_price" => 100,
|
10
|
+
"unit_name" => "mile"
|
11
|
+
)
|
12
|
+
category.name.should == "Mileage"
|
13
|
+
|
14
|
+
category.name = "Travel"
|
15
|
+
category = harvest.expense_categories.update(category)
|
16
|
+
category.name.should == "Travel"
|
17
|
+
|
18
|
+
harvest.expense_categories.delete(category)
|
19
|
+
harvest.expense_categories.all.select{|e| e.name == "Travel" }.should == []
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
it "allows adding, updating, and removing expenses" do
|
26
|
+
cassette("expenses2") do
|
27
|
+
category = harvest.expense_categories.create(
|
28
|
+
"name" => "Something Deductible",
|
29
|
+
"unit_price" => 100,
|
30
|
+
"unit_name" => "deduction"
|
31
|
+
)
|
32
|
+
|
33
|
+
client = harvest.clients.create(
|
34
|
+
"name" => "Tom's Butcher",
|
35
|
+
"details" => "Building API Widgets across the country"
|
36
|
+
)
|
37
|
+
|
38
|
+
project = harvest.projects.create(
|
39
|
+
"name" => "Expensing Project",
|
40
|
+
"active" => true,
|
41
|
+
"notes" => "project to test the api",
|
42
|
+
"client_id" => client.id
|
43
|
+
)
|
44
|
+
|
45
|
+
expense = harvest.expenses.create(
|
46
|
+
"notes" => "Drive to Chicago",
|
47
|
+
"total_cost" => 75.0,
|
48
|
+
"spent_at" => Time.utc(2009, 12, 28),
|
49
|
+
"expense_category_id" => category.id,
|
50
|
+
"project_id" => project.id
|
51
|
+
)
|
52
|
+
expense.notes.should == "Drive to Chicago"
|
53
|
+
|
54
|
+
expense.notes = "Off to Chicago"
|
55
|
+
expense = harvest.expenses.update(expense)
|
56
|
+
expense.notes.should == "Off to Chicago"
|
57
|
+
|
58
|
+
harvest.expenses.delete(expense)
|
59
|
+
harvest.expenses.all(Time.utc(2009, 12, 28)).select {|e| e.notes == "Off to Chicago"}.should == []
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Copied from feature
|
65
|
+
# @wip
|
66
|
+
# Scenario: Attaching a receipt to an Expense
|
67
|
+
# Given I am using the credentials from "./support/harvest_credentials.yml"
|
68
|
+
# When I create an expense category with the following:
|
69
|
+
# | name | Mileage |
|
70
|
+
# | unit_name | Miles |
|
71
|
+
# | unit_price | 0.485 |
|
72
|
+
# Then there should be an expense category "Mileage"
|
73
|
+
# When I create a client with the following:
|
74
|
+
# | name | Expense Client |
|
75
|
+
# When I create a project for the client "Expense Client" with the following:
|
76
|
+
# | name | Test Project |
|
77
|
+
# When I create an expense for the project "Test Project" with the category "Mileage" with the following:
|
78
|
+
# | notes | Drive to Chicago |
|
79
|
+
# | total_cost | 75.00 |
|
80
|
+
# | spent_at | 12/28/2009 |
|
81
|
+
# Then there should be an expense "Drive to Chicago" on "12/28/2009"
|
82
|
+
# When I attach the receipt "./support/fixtures/receipt.png" to the expense "Drive to Chicago" on "12/28/2009"
|
83
|
+
# Then there should be a receipt "./support/fixtures/receipt.png" attached to the expense "Drive to Chicago" on "12/28/2009"
|
84
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'harvest hardy client' do
|
4
|
+
before do
|
5
|
+
WebMock.disable_net_connect!
|
6
|
+
@time = Time.now
|
7
|
+
end
|
8
|
+
|
9
|
+
it "waits the alloted time out when over rate limit" do
|
10
|
+
stub_request(:get, /\/clients/).to_return({:status => ['503', 'Rate Limited'], :headers => {"Retry-After" => "5"}}, {:body => "[]", :status => 200})
|
11
|
+
hardy_harvest.clients.all
|
12
|
+
Time.now.should be_within(10).of(@time)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "waits a default time when over the rate limit" do
|
16
|
+
stub_request(:get, /\/clients/).to_return({:status => ['503', 'Rate Limited']}, {:body => "[]", :status => 200})
|
17
|
+
hardy_harvest.clients.all
|
18
|
+
Time.now.should be_within(20).of(@time)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "retries after known errors" do
|
22
|
+
stub_request(:get, /\/rate_limit_status/).to_return({:body => '{"lockout_seconds":2,"last_access_at":"2011-06-18T17:13:22+00:00","max_calls":100,"count":1,"timeframe_limit":15}'})
|
23
|
+
|
24
|
+
stub_request(:get, /\/clients/).to_return({:status => ['502', 'Bad Gateway']}).times(2).then.to_return({:body => "[]", :status => 200})
|
25
|
+
hardy_harvest.clients.all.should == []
|
26
|
+
|
27
|
+
stub_request(:get, /\/clients/).to_raise(Net::HTTPError.new("custom error", "")).times(2).then.to_return({:body => "[]", :status => 200})
|
28
|
+
hardy_harvest.clients.all.should == []
|
29
|
+
|
30
|
+
stub_request(:get, /\/clients/).to_return({:status => ['502', 'Bad Gateway']}).times(6).then.to_return({:body => "[]", :status => 200})
|
31
|
+
expect { hardy_harvest.clients.all }.to raise_error(Harvest::Unavailable)
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'harvest invoices' do
|
4
|
+
it 'allows adding, updating and removing categories' do
|
5
|
+
cassette('invoice1') do
|
6
|
+
cat = harvest.invoice_categories.create("name" => "New Category")
|
7
|
+
cat.name.should == "New Category"
|
8
|
+
|
9
|
+
cat.name = "Updated Category"
|
10
|
+
cat = harvest.invoice_categories.update(cat)
|
11
|
+
cat.name.should == "Updated Category"
|
12
|
+
|
13
|
+
harvest.invoice_categories.delete(cat)
|
14
|
+
harvest.invoice_categories.all.select {|c| c.name == "Updated Category" }.should == []
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'allows adding, updating and removing invoices' do
|
19
|
+
pending "having trouble following the API docs at http://www.getharvest.com/api/invoices"
|
20
|
+
|
21
|
+
cassette('invoice2') do
|
22
|
+
client = harvest.clients.create("name" => "Frannie's Factory")
|
23
|
+
project = harvest.projects.create("name" => "Invoiced Project1", "client_id" => client.id)
|
24
|
+
|
25
|
+
# invoice = harvest.invoices.create(
|
26
|
+
invoice = Harvest::Invoice.new(
|
27
|
+
"subject" => "Invoice for Frannie's Widgets",
|
28
|
+
"client_id" => client.id,
|
29
|
+
"issued_at" => "2011-03-31",
|
30
|
+
"due_at" => "2011-03-31",
|
31
|
+
"due_at_human_format" => "upon receipt",
|
32
|
+
|
33
|
+
"currency" => "United States Dollars - USD",
|
34
|
+
"number" => 1000,
|
35
|
+
"notes" => "Some notes go here",
|
36
|
+
"period_end" => "2011-03-31",
|
37
|
+
"period_start" => "2011-02-26",
|
38
|
+
"kind" => "free_form"
|
39
|
+
# "state" => "draft",
|
40
|
+
# "purchase_order" => nil,
|
41
|
+
# "tax" => nil,
|
42
|
+
# "tax2" => nil,
|
43
|
+
# "kind" => "free_form",
|
44
|
+
# "import_hours" => "no",
|
45
|
+
# "import_expenses" => "no"
|
46
|
+
# "line_items" => [Harvest::LineItem.new("kind" => "Service", "description" => "One item", "quantity" => 200, "unit_price" => "12.00")]
|
47
|
+
)
|
48
|
+
p invoice.to_json
|
49
|
+
invoice = harvest.invoices.create(invoice)
|
50
|
+
|
51
|
+
invoice.subject.should == "Invoice for Frannie's Widgets"
|
52
|
+
invoice.amount.should == "2400.0"
|
53
|
+
invoice.line_items.size.should == 1
|
54
|
+
|
55
|
+
invoice.subject = "Updated Invoice for Frannie"
|
56
|
+
invoice.line_items << Harvest::LineItem.new("kind" => "Service", "description" => "Two item", "quantity" => 10, "unit_price" => "2.00", "amount" => "20.0")
|
57
|
+
|
58
|
+
invoice = harvest.invoices.update(invoice)
|
59
|
+
invoice.subject.should == "Updated Invoice for Frannie"
|
60
|
+
invoice.amount.should == "2420.0"
|
61
|
+
invoice.line_items.size.should == 2
|
62
|
+
|
63
|
+
harvest.invoices.delete(invoice)
|
64
|
+
harvest.invoices.all.select {|p| p.number == "1000"}.should == []
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'harvest projects' do
|
4
|
+
it 'allows adding, updating and removing projects' do
|
5
|
+
cassette('project1') do
|
6
|
+
client = harvest.clients.create(
|
7
|
+
"name" => "Joe's Steam Cleaning",
|
8
|
+
"details" => "Building API Widgets across the country"
|
9
|
+
)
|
10
|
+
|
11
|
+
project = harvest.projects.create(
|
12
|
+
"name" => "Test Project",
|
13
|
+
"active" => true,
|
14
|
+
"notes" => "project to test the api",
|
15
|
+
"client_id" => client.id
|
16
|
+
)
|
17
|
+
project.name.should == "Test Project"
|
18
|
+
|
19
|
+
project.name = "Updated Project"
|
20
|
+
project = harvest.projects.update(project)
|
21
|
+
project.name.should == "Updated Project"
|
22
|
+
|
23
|
+
harvest.projects.delete(project)
|
24
|
+
harvest.projects.all.select {|p| p.name == "Updated Project"}.should == []
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'allows activating and deactivating clients' do
|
29
|
+
cassette('project2') do
|
30
|
+
client = harvest.clients.create(
|
31
|
+
"name" => "Joe's Steam Cleaning",
|
32
|
+
"details" => "Building API Widgets across the country"
|
33
|
+
)
|
34
|
+
|
35
|
+
project = harvest.projects.create(
|
36
|
+
"name" => "Test Project",
|
37
|
+
"active" => true,
|
38
|
+
"notes" => "project to test the api",
|
39
|
+
"client_id" => client.id
|
40
|
+
)
|
41
|
+
project.should be_active
|
42
|
+
|
43
|
+
project = harvest.projects.deactivate(project)
|
44
|
+
project.should_not be_active
|
45
|
+
|
46
|
+
project = harvest.projects.activate(project)
|
47
|
+
project.should be_active
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'harvest reporting' do
|
4
|
+
it 'allows project and people entry reporting' do
|
5
|
+
cassette("reports1") do
|
6
|
+
user = harvest.users.create(
|
7
|
+
"email" => "jane@example.com",
|
8
|
+
"first_name" => "Jane",
|
9
|
+
"last_name" => "Doe",
|
10
|
+
"password" => "secure"
|
11
|
+
)
|
12
|
+
client = harvest.clients.create("name" => "Tim's Dry Cleaning")
|
13
|
+
project1 = harvest.projects.create("name" => "Reporting Project1", "client_id" => client.id)
|
14
|
+
project2 = harvest.projects.create("name" => "Reporting Project2", "client_id" => client.id)
|
15
|
+
task1 = harvest.tasks.create("name" => "A billable task", "default_hourly_rate" => 120, "billable_by_default" => true)
|
16
|
+
task2 = harvest.tasks.create("name" => "A non billable task", "billable_by_default" => false)
|
17
|
+
|
18
|
+
harvest.task_assignments.create("project" => project1, "task" => task1)
|
19
|
+
harvest.task_assignments.create("project" => project2, "task" => task2)
|
20
|
+
harvest.user_assignments.create("project" => project1, "user" => user)
|
21
|
+
harvest.user_assignments.create("project" => project2, "user" => user)
|
22
|
+
|
23
|
+
entry1 = harvest.time.create("notes" => "billable entry1", "hours" => 3, "spent_at" => "12/28/2009", "task_id" => task1.id, "project_id" => project1.id, "of_user" => user.id)
|
24
|
+
entry2 = harvest.time.create("notes" => "non billable entry2", "hours" => 6, "spent_at" => "12/28/2009", "task_id" => task2.id, "project_id" => project2.id, "of_user" => user.id)
|
25
|
+
|
26
|
+
harvest.reports.time_by_project(project1, Time.utc(2009, 12, 20), Time.utc(2009,12,30)).first.should == entry1
|
27
|
+
|
28
|
+
harvest.reports.time_by_project(project1, Time.utc(2009, 12, 20), Time.utc(2009,12,30), :user => user).first.should == entry1
|
29
|
+
|
30
|
+
harvest.reports.time_by_project(project2, Time.utc(2009, 12, 20), Time.utc(2009,12,30), :user => user).first.should == entry2
|
31
|
+
|
32
|
+
harvest.reports.time_by_project(project2, Time.utc(2009, 12, 20), Time.utc(2009,12,30), :billable => true).should == []
|
33
|
+
harvest.reports.time_by_project(project2, Time.utc(2009, 12, 20), Time.utc(2009,12,30), :billable => false).first.should == entry2
|
34
|
+
|
35
|
+
harvest.reports.time_by_user(user, Time.utc(2009, 12, 20), Time.utc(2009,12,30)).map(&:id).should == [entry1, entry2].map(&:id)
|
36
|
+
|
37
|
+
harvest.reports.time_by_user(user, Time.utc(2009, 12, 20), Time.utc(2009,12,30), :project => project1).first.should == entry1
|
38
|
+
|
39
|
+
harvest.reports.time_by_user(user, Time.utc(2009, 12, 20), Time.utc(2009,12,30), :project => project2).first.should == entry2
|
40
|
+
|
41
|
+
harvest.reports.time_by_user(user, Time.utc(2009, 12, 20), Time.utc(2009,12,30), :billable => true).first.should == entry1
|
42
|
+
harvest.reports.time_by_user(user, Time.utc(2009, 12, 20), Time.utc(2009,12,30), :billable => false).first.should == entry2
|
43
|
+
|
44
|
+
client2 = harvest.clients.create("name" => "Phil's Sandwich Shop")
|
45
|
+
harvest.reports.projects_by_client(client).map(&:id).to_set.should == [project1, project2].map(&:id).to_set
|
46
|
+
harvest.reports.projects_by_client(client2).should == []
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'allows expense reporting' do
|
51
|
+
cassette("reports2") do
|
52
|
+
user = harvest.users.create(
|
53
|
+
"email" => "simon@example.com",
|
54
|
+
"first_name" => "Simon",
|
55
|
+
"last_name" => "Stir",
|
56
|
+
"password" => "secure"
|
57
|
+
)
|
58
|
+
|
59
|
+
category = harvest.expense_categories.create("name" => "Reporting category", "unit_price" => 100, "unit_name" => "deduction")
|
60
|
+
client = harvest.clients.create("name" => "Philip's Butcher")
|
61
|
+
project = harvest.projects.create("name" => "Expense Reporting Project", "client_id" => client.id)
|
62
|
+
harvest.user_assignments.create("project" => project, "user" => user)
|
63
|
+
|
64
|
+
expense = harvest.expenses.create(
|
65
|
+
"notes" => "Drive to Chicago",
|
66
|
+
"total_cost" => 75.0,
|
67
|
+
"spent_at" => Time.utc(2009, 12, 28),
|
68
|
+
"expense_category_id" => category.id,
|
69
|
+
"project_id" => project.id,
|
70
|
+
"user_id" => user.id
|
71
|
+
)
|
72
|
+
|
73
|
+
harvest.reports.expenses_by_user(user, Time.utc(2009, 12, 20), Time.utc(2009,12,30)).first.should == expense
|
74
|
+
|
75
|
+
my_user = harvest.users.all.detect {|u| u.email == credentials["username"]}
|
76
|
+
my_user.should_not be_nil
|
77
|
+
harvest.reports.expenses_by_user(my_user, Time.utc(2009, 12, 20), Time.utc(2009,12,30)).should == []
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|