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/Gemfile +6 -3
- data/HISTORY +30 -26
- data/README.md +3 -3
- data/Rakefile +2 -0
- data/VERSION +1 -1
- data/examples/project_create_script.rb +93 -0
- data/harvested.gemspec +18 -3
- data/lib/harvest/api/invoice_categories.rb +5 -5
- data/lib/harvest/api/invoice_payments.rb +31 -0
- data/lib/harvest/api/invoices.rb +27 -8
- data/lib/harvest/api/tasks.rb +27 -0
- data/lib/harvest/base.rb +78 -56
- data/lib/harvest/behavior/crud.rb +2 -1
- data/lib/harvest/expense.rb +2 -2
- data/lib/harvest/invoice.rb +56 -20
- data/lib/harvest/invoice_payment.rb +8 -0
- data/lib/harvest/time_entry.rb +2 -2
- data/lib/harvested.rb +6 -6
- data/spec/factories.rb +57 -0
- data/spec/functional/clients_spec.rb +10 -16
- data/spec/functional/invoice_payments_spec.rb +44 -0
- data/spec/functional/invoice_spec.rb +88 -40
- data/spec/functional/project_spec.rb +2 -8
- data/spec/functional/tasks_spec.rb +2 -8
- data/spec/harvest/expense_spec.rb +2 -2
- data/spec/harvest/invoice_payment_spec.rb +5 -0
- data/spec/harvest/invoice_spec.rb +9 -10
- data/spec/harvest/time_entry_spec.rb +3 -3
- data/spec/spec_helper.rb +3 -0
- data/spec/support/harvested_helpers.rb +1 -1
- metadata +58 -4
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
|
7
|
-
|
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
|
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
|
41
|
-
|
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@
|
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@
|
45
|
+
contact.email.should == "jane@example.com"
|
52
46
|
|
53
47
|
harvest.contacts.delete(contact)
|
54
|
-
harvest.contacts.all.select {|e| e.email == "jane@
|
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(
|
23
|
-
|
24
|
-
|
25
|
-
|
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"
|
28
|
-
"client_id"
|
29
|
-
"issued_at"
|
30
|
-
"due_at"
|
31
|
-
"
|
32
|
-
|
33
|
-
"
|
34
|
-
"
|
35
|
-
"
|
36
|
-
"
|
37
|
-
"
|
38
|
-
"
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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
|
-
|
56
|
-
invoice
|
57
|
-
|
58
|
-
invoice
|
59
|
-
invoice.
|
60
|
-
|
61
|
-
|
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
|
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
|
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
|
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
|
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 ==
|
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 ==
|
15
|
+
expense.spent_at.should == Date.new(2009, 12, 1)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
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
|