harvested 0.6.2 → 0.6.3

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/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