harvested 0.6.2 → 0.6.3

Sign up to get free protection for your applications and to get access to all the features.
data/lib/harvested.rb CHANGED
@@ -19,14 +19,14 @@ require 'harvest/timezones'
19
19
  require 'harvest/base'
20
20
 
21
21
  %w(crud activatable).each {|a| require "harvest/behavior/#{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}"}
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 invoice_payment).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 invoice_payments).each {|a| require "harvest/api/#{a}"}
24
24
 
25
25
  module Harvest
26
26
  VERSION = File.read(File.expand_path(File.join(File.dirname(__FILE__), '..', 'VERSION'))).strip
27
-
27
+
28
28
  class << self
29
-
29
+
30
30
  # Creates a standard client that will raise all errors it encounters
31
31
  #
32
32
  # == Options
@@ -38,7 +38,7 @@ module Harvest
38
38
  def client(subdomain, username, password, options = {})
39
39
  Harvest::Base.new(subdomain, username, password, options)
40
40
  end
41
-
41
+
42
42
  # Creates a hardy client that will retry common HTTP errors it encounters and sleep() if it determines it is over your rate limit
43
43
  #
44
44
  # == Options
@@ -66,4 +66,4 @@ module Harvest
66
66
  Harvest::HardyClient.new(client(subdomain, username, password, options), (retries || 5))
67
67
  end
68
68
  end
69
- end
69
+ end
data/spec/factories.rb ADDED
@@ -0,0 +1,57 @@
1
+ FactoryGirl.define do
2
+ sequence :name do |n|
3
+ "Joe's Steam Cleaning #{n}"
4
+ end
5
+
6
+ sequence :description do |n|
7
+ "Item #{n}"
8
+ end
9
+
10
+ sequence :project_name do |n|
11
+ "Joe's Steam Cleaning Project #{n}"
12
+ end
13
+
14
+ factory :client, class: Harvest::Client do
15
+ name
16
+ details "Steam Cleaning across the country"
17
+ end
18
+
19
+ factory :invoice, class: Harvest::Invoice do
20
+ subject "Invoice for Joe's Stream Cleaning"
21
+ client_id nil
22
+ issued_at "2011-03-31"
23
+ due_at "2011-03-31"
24
+ due_at_human_format "upon receipt"
25
+
26
+ currency "United States Dollars - USD"
27
+ sequence(:number)
28
+ notes "Some notes go here"
29
+ period_end "2011-03-31"
30
+ period_start "2011-02-26"
31
+ kind "free_form"
32
+ state "draft"
33
+ purchase_order nil
34
+ tax nil
35
+ tax2 nil
36
+ import_hours "no"
37
+ import_expenses "no"
38
+ line_items { [FactoryGirl.build(:line_item)] }
39
+ end
40
+
41
+ factory :line_item, class: Harvest::LineItem do
42
+ kind "Service"
43
+ description
44
+ quantity 200
45
+ unit_price "12.00"
46
+ end
47
+
48
+ factory :invoice_payment, class: Harvest::InvoicePayment do
49
+ paid_at Time.now
50
+ amount "0.00"
51
+ notes "Payment received"
52
+ end
53
+
54
+ factory :project, class: Harvest::Project do
55
+ name { generate(:project_name) }
56
+ end
57
+ end
@@ -2,12 +2,11 @@ require 'spec_helper'
2
2
 
3
3
  describe 'harvest clients' do
4
4
  it 'allows adding, updating and removing clients' do
5
+ client_attributes = FactoryGirl.attributes_for(:client)
6
+
5
7
  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"
8
+ client = harvest.clients.create(client_attributes)
9
+ client.name.should == client_attributes[:name]
11
10
 
12
11
  client.name = "Joe and Frank's Steam Cleaning"
13
12
  client = harvest.clients.update(client)
@@ -20,10 +19,7 @@ describe 'harvest clients' do
20
19
 
21
20
  it 'allows activating and deactivating clients' do
22
21
  cassette("client2") do
23
- client = harvest.clients.create(
24
- "name" => "Joe's Steam Cleaning",
25
- "details" => "Building API Widgets across the country"
26
- )
22
+ client = harvest.clients.create(FactoryGirl.attributes_for(:client))
27
23
  client.should be_active
28
24
 
29
25
  client = harvest.clients.deactivate(client)
@@ -37,21 +33,19 @@ describe 'harvest clients' do
37
33
  context "contacts" do
38
34
  it "allows adding, updating, and removing contacts" do
39
35
  cassette("client3") do
40
- client = harvest.clients.create(
41
- "name" => "Joe's Steam Cleaning",
42
- "details" => "Building API Widgets across the country"
43
- )
36
+ client = harvest.clients.create(FactoryGirl.attributes_for(:client))
37
+
44
38
  contact = harvest.contacts.create(
45
39
  "client_id" => client.id,
46
- "email" => "jane@doe.com",
40
+ "email" => "jane@example.com",
47
41
  "first_name" => "Jane",
48
42
  "last_name" => "Doe"
49
43
  )
50
44
  contact.client_id.should == client.id
51
- contact.email.should == "jane@doe.com"
45
+ contact.email.should == "jane@example.com"
52
46
 
53
47
  harvest.contacts.delete(contact)
54
- harvest.contacts.all.select {|e| e.email == "jane@doe.com" }.should == []
48
+ harvest.contacts.all.select {|e| e.email == "jane@example.com" }.should == []
55
49
  end
56
50
  end
57
51
  end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'harvest invoice payments' do
4
+ it 'allows retrieving existing invoice payments' do
5
+ cassette('invoice_payment1') do
6
+ client = harvest.clients.create(FactoryGirl.attributes_for(:client))
7
+ invoice = harvest.invoices.create(FactoryGirl.attributes_for(:invoice, :client_id => client.id))
8
+
9
+ payment = Harvest::InvoicePayment.new(FactoryGirl.attributes_for(:invoice_payment, :invoice_id => invoice.id, :amount => invoice.amount))
10
+ payment_saved = harvest.invoice_payments.create(payment)
11
+
12
+ payment_found = harvest.invoice_payments.find(invoice, payment_saved)
13
+
14
+ payment_found.should == payment_saved
15
+ end
16
+ end
17
+
18
+ it 'allows adding, and removing invoice payments' do
19
+ cassette('invoice_payment2') do
20
+ client = harvest.clients.create(FactoryGirl.attributes_for(:client))
21
+ invoice = harvest.invoices.create(FactoryGirl.attributes_for(:invoice, :client_id => client.id))
22
+
23
+ half_amount = (invoice.amount.to_f / 2)
24
+
25
+ payment1 = Harvest::InvoicePayment.new(FactoryGirl.attributes_for(:invoice_payment, :invoice_id => invoice.id, :amount => half_amount))
26
+ payment1 = harvest.invoice_payments.create(payment1)
27
+
28
+ invoice = harvest.invoices.find(invoice.id)
29
+ invoice.state.should == 'partial'
30
+
31
+ payment2 = Harvest::InvoicePayment.new(FactoryGirl.attributes_for(:invoice_payment, :invoice_id => invoice.id, :amount => half_amount))
32
+ payment2 = harvest.invoice_payments.create(payment2)
33
+
34
+ invoice = harvest.invoices.find(invoice.id)
35
+ invoice.state.should == 'paid'
36
+
37
+ harvest.invoice_payments.all(invoice).each { |ip| harvest.invoice_payments.delete(ip) }
38
+ harvest.invoice_payments.all(invoice).should be_empty
39
+
40
+ invoice = harvest.invoices.find(invoice.id)
41
+ invoice.state.should == 'open'
42
+ end
43
+ end
44
+ end
@@ -5,61 +5,109 @@ describe 'harvest invoices' do
5
5
  cassette('invoice1') do
6
6
  cat = harvest.invoice_categories.create("name" => "New Category")
7
7
  cat.name.should == "New Category"
8
-
8
+
9
9
  cat.name = "Updated Category"
10
10
  cat = harvest.invoice_categories.update(cat)
11
11
  cat.name.should == "Updated Category"
12
-
12
+
13
13
  harvest.invoice_categories.delete(cat)
14
14
  harvest.invoice_categories.all.select {|c| c.name == "Updated Category" }.should == []
15
15
  end
16
16
  end
17
-
17
+
18
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
19
  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(
20
+ client = harvest.clients.create(FactoryGirl.attributes_for(:client))
21
+
22
+ invoice = Harvest::Invoice.new(FactoryGirl.attributes_for(:invoice, :client_id => client.id))
23
+ invoice = harvest.invoices.create(invoice)
24
+
25
+ invoice.subject.should == "Invoice for Joe's Stream Cleaning"
26
+ invoice.amount.should == "2400.0"
27
+ invoice.line_items.size.should == 1
28
+
29
+ invoice.subject = "Updated Invoice for Joe"
30
+ invoice.line_items << FactoryGirl.build(:line_item)
31
+
32
+ invoice = harvest.invoices.update(invoice)
33
+ invoice.subject.should == "Updated Invoice for Joe"
34
+ invoice.amount.should == "4800.0"
35
+ invoice.line_items.size.should == 2
36
+
37
+ harvest.invoices.delete(invoice)
38
+ harvest.invoices.all.select {|p| p.number == "1000"}.should == []
39
+ end
40
+ end
41
+
42
+ it 'allows finding one invoice or all invoices with parameters' do
43
+ cassette('invoice3') do
44
+ client = harvest.clients.create(FactoryGirl.attributes_for(:client))
45
+
46
+ project_attributes = FactoryGirl.attributes_for(:project)
47
+ project_attributes[:client_id] = client.id
48
+
49
+ project = harvest.projects.create(project_attributes)
50
+ project.name.should == project_attributes[:name]
51
+
52
+ # Delete any existing invoices.
53
+ harvest.invoices.all.each {|i| harvest.invoices.delete(i.id)}
54
+
26
55
  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")]
56
+ "subject" => "Invoice for Frannie's Widgets",
57
+ "client_id" => client.id,
58
+ "issued_at" => "2011-03-31",
59
+ "due_at" => "2011-05-31",
60
+ "currency" => "United States Dollars - USD",
61
+ "number" => 1000,
62
+ "notes" => "Some notes go here",
63
+ "period_end" => "2011-03-31",
64
+ "period_start" => "2011-02-26",
65
+ "kind" => "free_form",
66
+ "state" => "draft",
67
+ "purchase_order" => nil,
68
+ "tax" => nil,
69
+ "tax2" => nil,
70
+ "kind" => "free_form",
71
+ "import_hours" => "no",
72
+ "import_expenses" => "no",
73
+ "line_items" => [Harvest::LineItem.new("kind" => "Service", "description" => "One item", "quantity" => 200, "unit_price" => "12.00")]
47
74
  )
48
- p invoice.to_json
49
75
  invoice = harvest.invoices.create(invoice)
50
-
76
+
51
77
  invoice.subject.should == "Invoice for Frannie's Widgets"
52
78
  invoice.amount.should == "2400.0"
53
79
  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
-
80
+ invoice.due_at.should == "2011-05-31"
81
+
82
+ invoice = harvest.invoices.find(invoice.id)
83
+ invoice.subject.should == "Invoice for Frannie's Widgets"
84
+ invoice.amount.should == "2400.0"
85
+ invoice.line_items.size.should == 1
86
+
87
+ invoices = harvest.invoices.all
88
+ invoices.count.should == 1
89
+
90
+ invoices = harvest.invoices.all(:status => 'draft')
91
+ invoices.count.should == 1
92
+
93
+ invoices = harvest.invoices.all(:status => 'closed')
94
+ invoices.count.should == 0
95
+
96
+ invoices = harvest.invoices.all(:status => 'draft', :page => 1)
97
+ invoices.count.should == 1
98
+
99
+ invoices = harvest.invoices.all(:timeframe => {:from => Date.today, :to => Date.today})
100
+ invoices.count.should == 1
101
+
102
+ invoices = harvest.invoices.all(:timeframe => {:from => '19690101', :to => '19690101'})
103
+ invoices.count.should == 0
104
+
105
+ invoices = harvest.invoices.all(:updated_since => Date.today)
106
+ invoices.count.should == 1
107
+
108
+ invoices = harvest.invoices.all(:updated_since => '21121231')
109
+ invoices.count.should == 0
110
+
63
111
  harvest.invoices.delete(invoice)
64
112
  harvest.invoices.all.select {|p| p.number == "1000"}.should == []
65
113
  end
@@ -3,10 +3,7 @@ require 'spec_helper'
3
3
  describe 'harvest projects' do
4
4
  it 'allows adding, updating and removing projects' do
5
5
  cassette('project1') do
6
- client = harvest.clients.create(
7
- "name" => "Joe's Steam Cleaning",
8
- "details" => "Building API Widgets across the country"
9
- )
6
+ client = harvest.clients.create(FactoryGirl.attributes_for(:client))
10
7
 
11
8
  project = harvest.projects.create(
12
9
  "name" => "Test Project",
@@ -27,10 +24,7 @@ describe 'harvest projects' do
27
24
 
28
25
  it 'allows activating and deactivating clients' do
29
26
  cassette('project2') do
30
- client = harvest.clients.create(
31
- "name" => "Joe's Steam Cleaning",
32
- "details" => "Building API Widgets across the country"
33
- )
27
+ client = harvest.clients.create(FactoryGirl.attributes_for(:client))
34
28
 
35
29
  project = harvest.projects.create(
36
30
  "name" => "Test Project",
@@ -22,10 +22,7 @@ describe 'harvest tasks' do
22
22
  context "task assignments" do
23
23
  it "allows adding, updating, and removing tasks from projects" do
24
24
  cassette('tasks2') do
25
- client = harvest.clients.create(
26
- "name" => "Joe's Steam Cleaning",
27
- "details" => "Building API Widgets across the country"
28
- )
25
+ client = harvest.clients.create(FactoryGirl.attributes_for(:client))
29
26
 
30
27
  project = harvest.projects.create(
31
28
  "name" => "Test Project2",
@@ -66,10 +63,7 @@ describe 'harvest tasks' do
66
63
 
67
64
  it "allows creating and assigning the task at the same time" do
68
65
  cassette('tasks3') do
69
- client = harvest.clients.create(
70
- "name" => "Joe's Steam Cleaning 2",
71
- "details" => "Building API Widgets across the country"
72
- )
66
+ client = harvest.clients.create(FactoryGirl.attributes_for(:client))
73
67
 
74
68
  project = harvest.projects.create(
75
69
  "name" => "Test Project3",
@@ -7,12 +7,12 @@ describe Harvest::Expense do
7
7
  it "should parse strings" do
8
8
  date = RUBY_VERSION =~ /1.8/ ? "12/01/2009" : "01/12/2009"
9
9
  expense = Harvest::Expense.new(:spent_at => date)
10
- expense.spent_at.should == Time.parse(date)
10
+ expense.spent_at.should == Date.parse(date)
11
11
  end
12
12
 
13
13
  it "should accept times" do
14
14
  expense = Harvest::Expense.new(:spent_at => Time.utc(2009, 12, 1))
15
- expense.spent_at.should == Time.utc(2009, 12, 1)
15
+ expense.spent_at.should == Date.new(2009, 12, 1)
16
16
  end
17
17
  end
18
18
  end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe Harvest::InvoicePayment do
4
+ it_behaves_like 'a json sanitizer', %w(cache_version)
5
+ end
@@ -6,42 +6,41 @@ describe Harvest::Invoice do
6
6
  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,100\nService,def,1.00,20.00,20.0,false,false,101\n")
7
7
  invoice.line_items.count.should == 2
8
8
  line_item = invoice.line_items.first
9
-
9
+
10
10
  line_item.kind.should == "Service"
11
11
  line_item.project_id.should == "100"
12
12
  end
13
-
13
+
14
14
  it 'parses csv into objects w/o projects' do
15
15
  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")
16
16
  invoice.line_items.count.should == 2
17
17
  line_item = invoice.line_items.first
18
-
18
+
19
19
  line_item.kind.should == "Service"
20
20
  line_item.description.should == "Abc"
21
21
  end
22
-
22
+
23
23
  it 'parses empty strings' do
24
24
  invoice = Harvest::Invoice.new(:line_items => "")
25
25
  invoice.line_items.should == []
26
26
  end
27
-
27
+
28
28
  it 'accepts rich objects' do
29
29
  Harvest::Invoice.new(:line_items => [Harvest::LineItem.new(:kind => "Service")]).line_items.count.should == 1
30
30
  Harvest::Invoice.new(:line_items => Harvest::LineItem.new(:kind => "Service")).line_items.count.should == 1
31
31
  end
32
-
32
+
33
33
  it 'accepts nil' do
34
34
  Harvest::Invoice.new(:line_items => nil).line_items.should == []
35
35
  end
36
36
  end
37
-
37
+
38
38
  context "as_json" do
39
39
  it 'encodes line items csv' 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
-
44
- invoice.as_json["doc"]["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"
43
+ 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"
45
44
  end
46
45
  end
47
- end
46
+ end